Merge pull request #123 from Servostar/integrate-library
Integrate library
This commit is contained in:
commit
db501b4b9e
|
@ -0,0 +1,3 @@
|
|||
BasedOnStyle: Google
|
||||
IndentWidth: 4
|
||||
PointerAlignment: Left
|
|
@ -18,3 +18,4 @@ build
|
|||
CTestTestfile.cmake
|
||||
DartConfiguration.tcl
|
||||
*.cmake
|
||||
*.ll
|
||||
|
|
|
@ -72,9 +72,12 @@ add_custom_command(OUTPUT ${YACC_GENERATED_SOURCE_FILE}
|
|||
# Setup Glib 2.0 #
|
||||
# ------------------------------------------------ #
|
||||
|
||||
include(FindPkgConfig)
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_search_module(GLIB REQUIRED IMPORTED_TARGET glib-2.0)
|
||||
|
||||
link_libraries(PkgConfig::GLIB)
|
||||
|
||||
# ------------------------------------------------ #
|
||||
# TOML-C99 #
|
||||
# ------------------------------------------------ #
|
||||
|
@ -95,6 +98,30 @@ set_target_properties(tomlc99
|
|||
|
||||
include_directories(${PROJECT_SOURCE_DIR}/dep/tomlc99)
|
||||
|
||||
link_libraries(tomlc99)
|
||||
|
||||
# ------------------------------------------------ #
|
||||
# Standard library #
|
||||
# ------------------------------------------------ #
|
||||
|
||||
add_subdirectory(lib)
|
||||
|
||||
# ------------------------------------------------ #
|
||||
# LLVM backend #
|
||||
# ------------------------------------------------ #
|
||||
|
||||
# Fetch LLVM link configuration
|
||||
execute_process(COMMAND llvm-config --libs all
|
||||
OUTPUT_VARIABLE LLVM_LIBS)
|
||||
# Strip whitespace from output
|
||||
string(STRIP "${LLVM_LIBS}" LLVM_LIBS)
|
||||
# Link all targets to LLVM
|
||||
link_libraries(${LLVM_LIBS})
|
||||
|
||||
execute_process(COMMAND llvm-config --includedir
|
||||
OUTPUT_VARIABLE LLVM_INCLUDE_DIR)
|
||||
string(STRIP "${LLVM_INCLUDE_DIR}" LLVM_INCLUDE_DIR)
|
||||
include_directories(${LLVM_INCLUDE_DIR})
|
||||
|
||||
# ------------------------------------------------ #
|
||||
# Source #
|
||||
|
@ -126,10 +153,6 @@ set_target_properties(release
|
|||
OUTPUT_NAME "gsc"
|
||||
RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/release)
|
||||
|
||||
target_link_libraries(release PkgConfig::GLIB)
|
||||
|
||||
target_link_libraries(release tomlc99)
|
||||
|
||||
# FIXME: cannot compile with /O2 because of /RTC1 flag
|
||||
if (MSVC)
|
||||
set(RELEASE_FLAGS)
|
||||
|
@ -165,10 +188,6 @@ set_target_properties(debug
|
|||
OUTPUT_NAME "gsc"
|
||||
RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/debug)
|
||||
|
||||
target_link_libraries(debug PkgConfig::GLIB)
|
||||
|
||||
target_link_libraries(debug tomlc99)
|
||||
|
||||
if (MSVC)
|
||||
set(DEBUG_FLAGS /DEBUG)
|
||||
else()
|
||||
|
@ -198,10 +217,6 @@ set_target_properties(check
|
|||
OUTPUT_NAME "gsc"
|
||||
RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/check)
|
||||
|
||||
target_link_libraries(check PkgConfig::GLIB)
|
||||
|
||||
target_link_libraries(check tomlc99)
|
||||
|
||||
if (MSVC)
|
||||
set(CHECK_FLAGS /DEBUG /WX)
|
||||
else()
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
FROM servostar/gemstone:sdk-0.2.4-alpine-3.19.1
|
||||
FROM servostar/gemstone:sdk-0.2.5-alpine-3.19.1
|
||||
LABEL authors="servostar"
|
||||
LABEL version="0.2.4"
|
||||
LABEL version="0.2.5"
|
||||
LABEL description="docker image for setting up the build pipeline on SDK"
|
||||
LABEL website="https://github.com/Servostar/gemstone"
|
||||
|
||||
|
@ -10,7 +10,9 @@ COPY --chown=lorang CMakeLists.txt /home/lorang/
|
|||
COPY --chown=lorang run-check-test.sh /home/lorang/
|
||||
COPY --chown=lorang .env /home/lorang/
|
||||
COPY --chown=lorang run-docker-build.sh /home/lorang/
|
||||
COPY --chown=lorang run-lib-build.sh /home/lorang/
|
||||
COPY --chown=lorang dep /home/lorang/dep
|
||||
COPY --chown=lorang lib /home/lorang/lib
|
||||
COPY --chown=lorang .git /home/lorang/.git
|
||||
|
||||
RUN cmake .
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
cmake_minimum_required(VERSION 3.15...3.25)
|
||||
|
||||
project(gemstone
|
||||
VERSION 0.1.0
|
||||
DESCRIPTION "programming language compiler lld c++ layer"
|
||||
LANGUAGES CXX)
|
||||
|
||||
set(GEMSTONE_BINARY_DIR ${PROJECT_SOURCE_DIR}/../../bin)
|
||||
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
|
||||
# ------------------------------------------------ #
|
||||
# LLVM #
|
||||
# ------------------------------------------------ #
|
||||
|
||||
# Fetch LLVM link configuration
|
||||
execute_process(COMMAND llvm-config --libs all
|
||||
OUTPUT_VARIABLE LLVM_LIBS)
|
||||
# Strip whitespace from output
|
||||
string(STRIP "${LLVM_LIBS}" LLVM_LIBS)
|
||||
# Link all targets to LLVM
|
||||
link_libraries(${LLVM_LIBS})
|
||||
|
||||
execute_process(COMMAND llvm-config --includedir
|
||||
OUTPUT_VARIABLE LLVM_INCLUDE_DIR)
|
||||
string(STRIP "${LLVM_INCLUDE_DIR}" LLVM_INCLUDE_DIR)
|
||||
include_directories(${LLVM_INCLUDE_DIR})
|
||||
|
||||
file(GLOB_RECURSE SOURCE_FILES *.cpp)
|
||||
|
||||
add_library(lldcl ${SOURCE_FILES})
|
||||
target_link_libraries(lldcl)
|
||||
set_target_properties(lldcl
|
||||
PROPERTIES
|
||||
OUTPUT_NAME "lldcl"
|
||||
ARCHIVE_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/dep)
|
|
@ -0,0 +1,50 @@
|
|||
//
|
||||
// Created by servostar on 6/8/24.
|
||||
//
|
||||
// based on: https://github.com/llvm/llvm-project/blob/main/lld/unittests/AsLibAll/AllDrivers.cpp
|
||||
// https://github.com/numba/llvmlite/blob/main/ffi/linker.cpp
|
||||
|
||||
#include <lld/Common/Driver.h>
|
||||
#include <llvm/Support/raw_ostream.h>
|
||||
|
||||
/*
|
||||
* Gemstone supports Windows (COFF, MinGW) and GNU/Linux (ELF)
|
||||
*/
|
||||
|
||||
LLD_HAS_DRIVER(coff)
|
||||
LLD_HAS_DRIVER(elf)
|
||||
LLD_HAS_DRIVER(mingw)
|
||||
// LLD_HAS_DRIVER(macho)
|
||||
// LLD_HAS_DRIVER(wasm)
|
||||
|
||||
#define LLD_COFF_ELF_MINGW_DRIVER { {lld::WinLink, &lld::coff::link}, {lld::Gnu, &lld::elf::link}, {lld::MinGW, &lld::mingw::link} }
|
||||
|
||||
extern "C" {
|
||||
|
||||
/**
|
||||
* @brief C-wrapper for lldMain which is written in C++
|
||||
* @param Argc
|
||||
* @param Argv
|
||||
* @param outstr
|
||||
* @return
|
||||
*/
|
||||
int lld_main(int Argc, const char **Argv, const char **outstr) {
|
||||
// StdOut
|
||||
std::string stdout;
|
||||
llvm::raw_string_ostream stdout_stream(stdout);
|
||||
|
||||
// StdErr
|
||||
std::string stderr;
|
||||
llvm::raw_string_ostream stderr_stream(stderr);
|
||||
|
||||
// convert arguments
|
||||
std::vector<const char *> Args(Argv, Argv + Argc);
|
||||
|
||||
lld::Result result = lld::lldMain(Args, stdout_stream, stderr_stream, LLD_COFF_ELF_MINGW_DRIVER);
|
||||
|
||||
*outstr = strdup(stderr.append(stdout).c_str());
|
||||
|
||||
return result.retCode;
|
||||
}
|
||||
|
||||
} // extern "C"
|
|
@ -0,0 +1,18 @@
|
|||
//
|
||||
// Created by servostar on 6/10/24.
|
||||
//
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
extern void swap(float*, float*);
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
float a = 2.0;
|
||||
float b = 7.0;
|
||||
|
||||
swap(&a, &b);
|
||||
|
||||
printf("%f, %f\n", a, b);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
|
||||
fun swap(in out float: a, in out float: b) {
|
||||
float: c = a
|
||||
a = b
|
||||
b = c
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
//
|
||||
// Created by servostar on 6/10/24.
|
||||
//
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
extern void fib(int*, int);
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
|
||||
for (int i = 0; i < 7; i++) {
|
||||
int r = 0;
|
||||
fib(&r, i);
|
||||
printf("%d\n", r);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
|
||||
fun fib(in int: x, out int: c) {
|
||||
|
||||
int: i = 0
|
||||
int: k = 1
|
||||
int: p = 1
|
||||
|
||||
while x < i {
|
||||
int: t = k
|
||||
k = k + p
|
||||
p = t
|
||||
|
||||
i = i + 1
|
||||
}
|
||||
|
||||
c = k
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
cmake_minimum_required(VERSION 3.15...3.25)
|
||||
|
||||
project(gemstone_stdlib
|
||||
VERSION 0.1.0
|
||||
DESCRIPTION "gemstone programming language standard library"
|
||||
LANGUAGES C)
|
||||
|
||||
set(CMAKE_C_STANDARD 23)
|
||||
set(CMAKE_C_STANDARD_REQUIRED TRUE)
|
||||
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
|
||||
include_directories(${PROJECT_SOURCE_DIR}/src)
|
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/../bin/std")
|
||||
|
||||
# add native module libraries
|
||||
|
||||
file(GLOB_RECURSE STDLIB_IO_SOURCE_FILES src/io/*.c)
|
||||
add_library(io ${STDLIB_IO_SOURCE_FILES})
|
||||
|
||||
file(GLOB_RECURSE STDLIB_MEM_SOURCE_FILES src/mem/*.c)
|
||||
add_library(mem ${STDLIB_MEM_SOURCE_FILES})
|
||||
|
||||
file(GLOB_RECURSE STDLIB_OS_SOURCE_FILES src/os/*.c)
|
||||
add_library(os ${STDLIB_OS_SOURCE_FILES})
|
|
@ -0,0 +1,16 @@
|
|||
[project]
|
||||
name = "gemstone standard library"
|
||||
version = "0.1.0"
|
||||
description = "Cross platform standard library for thr gemstone programming language."
|
||||
license = "GPL-2.0"
|
||||
authors = [ "Sven Vogel <sven.vogel123@web.de>" ]
|
||||
|
||||
[target.release]
|
||||
root = "src/std.gem"
|
||||
mode = "library"
|
||||
output = "bin"
|
||||
archive = "archive"
|
||||
print_ast = false
|
||||
print_asm = false
|
||||
print_ir = false
|
||||
opt = 3
|
|
@ -0,0 +1,7 @@
|
|||
|
||||
import "def.gsc"
|
||||
|
||||
type unsigned int: bool
|
||||
|
||||
static bool: TRUE = 1
|
||||
static bool: FALSE = 0
|
|
@ -0,0 +1,14 @@
|
|||
//
|
||||
// Created by servostar on 6/3/24.
|
||||
//
|
||||
|
||||
#ifndef GEMSTONE_STD_LIB_CAPI_H
|
||||
#define GEMSTONE_STD_LIB_CAPI_H
|
||||
|
||||
#if defined(_WIN32) || defined (_WIN64)
|
||||
#define PLATFORM_WINDOWS
|
||||
#elif defined(unix) || defined(__unix) || defined(__unix__) || defined(__linux__)
|
||||
#define PLATFORM_POSIX
|
||||
#endif
|
||||
|
||||
#endif // GEMSTONE_STD_LIB_CAPI_H
|
|
@ -0,0 +1,38 @@
|
|||
# Author: Sven Vogel
|
||||
# Edited: 25.05.2024
|
||||
# License: GPL-2.0
|
||||
|
||||
# ,----------------------------------------.
|
||||
# | Standard Type definitions |
|
||||
# `----------------------------------------`
|
||||
|
||||
# Unsigned integrals
|
||||
|
||||
type unsigned half half int: u8
|
||||
type unsigned half int: u16
|
||||
type unsigned int: u32
|
||||
type unsigned double int: u64
|
||||
type unsigned double double int: u128
|
||||
|
||||
# Signed integrals
|
||||
|
||||
type signed u8: i8
|
||||
type signed u16: i16
|
||||
type signed u32: i32
|
||||
type signed u64: i64
|
||||
type signed u128: i128
|
||||
|
||||
# IEEE-754 floating point
|
||||
|
||||
type signed half float: f16
|
||||
type signed float: f32
|
||||
type signed double float: f64
|
||||
type signed double double float: f128
|
||||
|
||||
# String constant
|
||||
|
||||
type ref u8: cstr
|
||||
|
||||
# C style void pointer replacement
|
||||
|
||||
type ref u8: ptr
|
|
@ -0,0 +1,28 @@
|
|||
// Author: Sven Vogel
|
||||
// Edited: 25.05.2024
|
||||
// License: GPL-2.0
|
||||
|
||||
#ifndef GEMSTONE_STD_LIB_DEF_H_
|
||||
#define GEMSTONE_STD_LIB_DEF_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
typedef uint8_t u8;
|
||||
typedef uint16_t u16;
|
||||
typedef uint32_t u32;
|
||||
typedef uint64_t u64;
|
||||
|
||||
typedef int8_t i8;
|
||||
typedef int16_t i16;
|
||||
typedef int32_t i32;
|
||||
typedef int64_t i64;
|
||||
|
||||
typedef float f32;
|
||||
typedef double f64;
|
||||
|
||||
typedef u8* cstr;
|
||||
|
||||
typedef u8* ptr;
|
||||
|
||||
#endif // GEMSTONE_STD_LIB_DEF_H_
|
|
@ -0,0 +1,10 @@
|
|||
//
|
||||
// Created by servostar on 6/10/24.
|
||||
//
|
||||
|
||||
extern void entry(void);
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
entry();
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
# Author: Sven Vogel
|
||||
# Edited: 25.05.2024
|
||||
# License: GPL-2.0
|
||||
|
||||
# ,----------------------------------------.
|
||||
# | Generic Input/Output |
|
||||
# `----------------------------------------`
|
||||
|
||||
import "def.gsc"
|
||||
|
||||
# platform specific handle to an I/O device
|
||||
# can be a file, buffer, window or something else
|
||||
# NOTE: this reference is not meant to be dereferenced
|
||||
# which can lead to errors and undefined behavior
|
||||
type ptr: handle
|
||||
|
||||
# Returns a handle to this processes standard input I/O handle
|
||||
# -- Implementation note
|
||||
# On Linux this will return 0 as is it convention under UNIX (see: https://www.man7.org/linux/man-pages/man3/stdin.3.html)
|
||||
# On Windows the library will call `GetStdHandle(STD_INPUT_HANDLE)`
|
||||
fun getStdinHandle(out handle: stdin)
|
||||
|
||||
# Returns a handle to this processes standard input I/O handle
|
||||
# -- Implementation note
|
||||
# On Linux this will return 1 as is it convention under UNIX (see: https://www.man7.org/linux/man-pages/man3/stdout.3.html)
|
||||
# On Windows the library will call `GetStdHandle(STD_OUTPUT_HANDLE)`
|
||||
fun getStdoutHandle(out handle: stdout)
|
||||
|
||||
# Returns a handle to this processes standard input I/O handle
|
||||
# -- Implementation note
|
||||
# On Linux this will return 1 as is it convention under UNIX (see: https://www.man7.org/linux/man-pages/man3/stderr.3.html)
|
||||
# On Windows the library will call `GetStdHandle(STD_OUTPUT_HANDLE)`
|
||||
fun getStderrHandle(out handle: stderr)
|
||||
|
||||
# Write `len` number of bytes from `buf` into the I/O resource specified
|
||||
# by `dev`. Returns the number of bytes written.
|
||||
# -- Implementation note
|
||||
# On Linux this will use the syscall write
|
||||
# On Windows this will use the WriteFile function
|
||||
fun writeBytes(in handle: dev, in ref u8: buf, in u32: len)(out u32: written)
|
||||
|
||||
# Read atmost `len` bytes to `buf` from the I/O resource specified by `dev`
|
||||
# Returns the number of read bytes in `written`
|
||||
# -- Implementation note
|
||||
# On Linux this will use the syscall read
|
||||
# On Windows this will use the ReadFile function
|
||||
fun readBytes(in handle: dev, in ref u8: buf, in u32: len)(out u32: read)
|
||||
|
||||
# Flushes the buffers of the I/O resource specified by `dev`
|
||||
# -- Implementation note
|
||||
# On Linux this will use the fsync function
|
||||
# On Windows this will use the FlushFileBuffers function
|
||||
fun flush(in handle: dev)
|
|
@ -0,0 +1,21 @@
|
|||
|
||||
#ifndef GEMSTONE_STD_LIB_IO_H_
|
||||
#define GEMSTONE_STD_LIB_IO_H_
|
||||
|
||||
#include <def/api.h>
|
||||
|
||||
typedef ptr handle;
|
||||
|
||||
void getStdinHandle(handle* stdin);
|
||||
|
||||
void getStdoutHandle(handle* stdout);
|
||||
|
||||
void getStderrHandle(handle* stderr);
|
||||
|
||||
void writeBytes(handle dev, u8* buf, u32 len, u32* written);
|
||||
|
||||
void readBytes(handle dev, u8* buf, u32 len, u32* read);
|
||||
|
||||
void flush(handle dev);
|
||||
|
||||
#endif //GEMSTONE_STD_LIB_IO_H_
|
|
@ -0,0 +1,75 @@
|
|||
|
||||
#include <io/api.h>
|
||||
#include <capi.h>
|
||||
|
||||
#if defined(PLATFORM_WINDOWS)
|
||||
|
||||
// Compile for Windows
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
// FIXME: error in case GetStdHandle return INVALID_HANDLE_VALUE
|
||||
// FIXME: error in case functions return 0
|
||||
|
||||
void getStdinHandle(handle* stdin) {
|
||||
*stdin = (handle) GetStdHandle(STD_INPUT_HANDLE);
|
||||
}
|
||||
|
||||
void getStdoutHandle(handle* stdout) {
|
||||
*stdout = (handle) GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
}
|
||||
|
||||
void getStderrHandle(handle* stderr) {
|
||||
*stderr = (handle) GetStdHandle(STD_ERROR_HANDLE);
|
||||
}
|
||||
|
||||
void writeBytes(handle dev, u8* buf, u32 len, u32* bytesWritten) {
|
||||
WriteFile((HANDLE) dev, buf, len, bytesRead, NULL);
|
||||
}
|
||||
|
||||
void readBytes(handle dev, u8* buf, u32 len, u32* bytesRead) {
|
||||
ReadFile((HANDLE) dev, buf, len, bytesRead, NULL);
|
||||
}
|
||||
|
||||
void flush(handle dev) {
|
||||
FlushFileBuffers((HANDLE) dev);
|
||||
}
|
||||
|
||||
#elif defined(PLATFORM_POSIX)
|
||||
|
||||
// Compile for Linux and BSD
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
|
||||
// savely cast a 64-bit pointer down to a 32-bit value
|
||||
// this assumes that 64-bit system will use 32-bit handles
|
||||
// which are stored as 64-bit by zero extending
|
||||
#define TO_INT(x) ((int)(long int)(x))
|
||||
|
||||
void getStdinHandle(handle* stdin) {
|
||||
*stdin = (handle) STDIN_FILENO;
|
||||
}
|
||||
|
||||
void getStdoutHandle(handle* stdout) {
|
||||
*stdout = (handle) STDOUT_FILENO;
|
||||
}
|
||||
|
||||
void getStderrHandle(handle* stderr) {
|
||||
*stderr = (handle) STDERR_FILENO;
|
||||
}
|
||||
|
||||
void writeBytes(handle dev, u8* buf, u32 len, u32* bytesWritten) {
|
||||
*bytesWritten = write(TO_INT(dev), buf, len);
|
||||
}
|
||||
|
||||
void readBytes(handle dev, u8* buf, u32 len, u32* bytesRead) {
|
||||
*bytesRead = read(TO_INT(dev), buf, len);
|
||||
}
|
||||
|
||||
void flush(handle dev) {
|
||||
fsync(TO_INT(dev));
|
||||
}
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,27 @@
|
|||
# Author: Sven Vogel
|
||||
# Edited: 25.05.2024
|
||||
# License: GPL-2.0
|
||||
|
||||
# ,----------------------------------------.
|
||||
# | Memory Management |
|
||||
# `----------------------------------------`
|
||||
|
||||
import "def.gsc"
|
||||
|
||||
# Allocate `len` bytes of heap memory
|
||||
# Returns a pointer to the memory as `ptr`
|
||||
fun heap_alloc(in u32: len)(out ref u8: ptr)
|
||||
|
||||
# Rellocate `len` bytes of heap memory
|
||||
# Returns a pointer to the memory as `ptr`
|
||||
fun heap_realloc(in u32: len, in out ref u8: ptr)
|
||||
|
||||
# Free a block of memory
|
||||
fun heap_free(in ref u8: ptr)
|
||||
|
||||
# Copy `len` bytes from `dst` into `src`
|
||||
fun copy(in ref u8: dst, in ref u8: src, in u32: len)
|
||||
|
||||
# Fill `len` bytes of `dst` with `byte`
|
||||
fun fill(in ref u8: dst, in u8: byte, in u32: len)
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
|
||||
#ifndef GEMSTONE_STD_LIB_MEM_H_
|
||||
#define GEMSTONE_STD_LIB_MEM_H_
|
||||
|
||||
#include <def/api.h>
|
||||
|
||||
void heap_alloc(u32 len, u8** ptr);
|
||||
|
||||
void heap_realloc(u32 len, u8** ptr);
|
||||
|
||||
void heap_free(u8* ptr);
|
||||
|
||||
void copy(u8* dst, u8* src, u32 len);
|
||||
|
||||
void fill(u8* dst, u8 byte, u32 len);
|
||||
|
||||
#endif // GEMSTONE_STD_LIB_MEM_H_
|
|
@ -0,0 +1,52 @@
|
|||
|
||||
#include <mem/api.h>
|
||||
#include <capi.h>
|
||||
|
||||
#if defined(PLATFORM_WINDOWS)
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
#define HEAP_API_GLOBAL_FLAGS HEAP_ZERO_MEMORY | HEAP_GENERATE_EXCEPTIONS
|
||||
|
||||
void heap_alloc(u32 len, u8** ptr) {
|
||||
HANDLE heap = GetProcessHeap();
|
||||
*ptr = HeapAlloc(heap, HEAP_API_GLOBAL_FLAGS, len);
|
||||
}
|
||||
|
||||
void heap_realloc(u32 len, u8** ptr) {
|
||||
HANDLE heap = GetProcessHeap();
|
||||
*ptr = HeapReAlloc(heap, HEAP_API_GLOBAL_FLAGS, *ptr, len);
|
||||
}
|
||||
|
||||
void heap_free(u8* ptr) {
|
||||
HANDLE heap = GetProcessHeap();
|
||||
HeapFree(heap, ptr);
|
||||
}
|
||||
|
||||
#elif defined(PLATFORM_POSIX)
|
||||
|
||||
#include <malloc.h>
|
||||
|
||||
void heap_alloc(u32 len, u8** ptr) {
|
||||
*ptr = malloc(len);
|
||||
}
|
||||
|
||||
void heap_realloc(u32 len, u8** ptr) {
|
||||
*ptr = realloc(*ptr, len);
|
||||
}
|
||||
|
||||
void heap_free(u8* ptr) {
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
void copy(u8* dst, u8* src, u32 len) {
|
||||
memcpy(dst, src, len);
|
||||
}
|
||||
|
||||
void fill(u8* dst, u8 byte, u32 len) {
|
||||
memset(dst, byte, len);
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
# Author: Sven Vogel
|
||||
# Edited: 03.06.2024
|
||||
# License: GPL-2.0
|
||||
|
||||
# ,----------------------------------------.
|
||||
# | Operating System |
|
||||
# `----------------------------------------`
|
||||
|
||||
import "def.gsc"
|
||||
|
||||
# Return a hard coded C string identifying the underlying operating system
|
||||
# Will return one of the following:
|
||||
# - "windows" (for Windows 7, Windows 10 and Windows 11)
|
||||
# - "unix" (for GNU/Linux and BSD)
|
||||
fun getPlatformName(out cstr: name)
|
||||
|
||||
# Return a C string to the value of an environment varible named
|
||||
# after the C string name.
|
||||
fun getEnvVar(in cstr: name)(out cstr: value)
|
||||
|
||||
# Set the value of an environment variable with name to value.
|
||||
fun setEnvVar(in cstr: name, in cstr: value)
|
||||
|
||||
# Unset a specific environment variable
|
||||
fun unsetEnvVar(in cstr: name)
|
|
@ -0,0 +1,18 @@
|
|||
//
|
||||
// Created by servostar on 6/3/24.
|
||||
//
|
||||
|
||||
#ifndef GEMSTONE_STD_LIB_OS_H
|
||||
#define GEMSTONE_STD_LIB_OS_H
|
||||
|
||||
#include <def/api.h>
|
||||
|
||||
void getPlatformName(cstr* name);
|
||||
|
||||
void getEnvVar(cstr name, cstr* value);
|
||||
|
||||
void setEnvVar(cstr name, cstr value);
|
||||
|
||||
void unsetEnvVar(cstr name);
|
||||
|
||||
#endif // GEMSTONE_STD_LIB_OS_H
|
|
@ -0,0 +1,36 @@
|
|||
//
|
||||
// Created by servostar on 6/3/24.
|
||||
//
|
||||
|
||||
#include <capi.h>
|
||||
#include <os/api.h>
|
||||
|
||||
#if defined(PLATFORM_WINDOWS)
|
||||
|
||||
void getPlatformName(cstr* name) {
|
||||
*name = (u8*) "windows";
|
||||
}
|
||||
|
||||
#elif defined(PLATFORM_POSIX)
|
||||
|
||||
void getPlatformName(cstr * name) {
|
||||
*name = (u8*) "posix";
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// Implementation based on libc
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
void getEnvVar(cstr name, cstr* value) {
|
||||
*value = (cstr) getenv((char*) name);
|
||||
}
|
||||
|
||||
void setEnvVar(cstr name, cstr value) {
|
||||
setenv((char*) name, (char*) value, true);
|
||||
}
|
||||
|
||||
void unsetEnvVar(cstr name) {
|
||||
unsetenv((char*) name);
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
# Author: Sven Vogel
|
||||
# Edited: 25.05.2024
|
||||
# License: GPL-2.0
|
||||
|
||||
# ,----------------------------------------.
|
||||
# | Gemstone Standard Library |
|
||||
# `----------------------------------------`
|
||||
|
||||
# standard type definitions
|
||||
import "def.gsc"
|
||||
|
||||
# I/O operations
|
||||
import "io.gsc"
|
||||
|
||||
# memory management
|
||||
import "mem.gsc"
|
|
@ -17,6 +17,8 @@ if [ ! $? -eq 0 ]; then
|
|||
exit 1
|
||||
fi
|
||||
|
||||
sh -c ./run-lib-build.sh
|
||||
|
||||
echo "+--------------------------------------+"
|
||||
echo "| RUNNING CODE CHECK |"
|
||||
echo "+--------------------------------------+"
|
||||
|
|
|
@ -47,7 +47,7 @@ echo "+--------------------------------------+"
|
|||
echo "| RUNNING check test |"
|
||||
echo "+--------------------------------------+"
|
||||
|
||||
docker run servostar/gemstone:devkit-"$SDK" sh run-check-test.sh
|
||||
docker run --rm --name "devkit-$SDK-check-test"- servostar/gemstone:devkit-"$SDK" sh run-check-test.sh
|
||||
if [ ! $? -eq 0 ]; then
|
||||
echo "===> failed to run build or checks"
|
||||
exit 1
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Author: Sven Vogel
|
||||
# Created: 25.05.2024
|
||||
# Description: Builds the standard library into bin/std
|
||||
|
||||
echo "+--------------------------------------+"
|
||||
echo "| CONFIGURE STD LIBRARY |"
|
||||
echo "+--------------------------------------+"
|
||||
|
||||
cmake lib
|
||||
if [ ! $? -eq 0 ]; then
|
||||
echo "===> failed to configure build"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "+--------------------------------------+"
|
||||
echo "| BUILD STD LIBRARY |"
|
||||
echo "+--------------------------------------+"
|
||||
|
||||
cd lib || exit 1
|
||||
make -B
|
||||
if [ ! $? -eq 0 ]; then
|
||||
echo "===> failed to build standard library"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "+--------------------------------------+"
|
||||
echo "| successfully build standard library |"
|
||||
echo "+--------------------------------------+"
|
|
@ -1,11 +1,11 @@
|
|||
FROM alpine:3.19.1
|
||||
LABEL authors="servostar"
|
||||
LABEL version="0.2.4"
|
||||
LABEL version="0.2.5"
|
||||
LABEL description="base image for building the gemstone programming language compiler"
|
||||
LABEL website="https://github.com/Servostar/gemstone"
|
||||
|
||||
# install dependencies
|
||||
RUN apk add build-base gcc make cmake bison flex git python3 graphviz glib glib-dev
|
||||
RUN apk add build-base gcc make cmake bison flex git python3 graphviz glib glib-dev llvm17-libs llvm17-dev
|
||||
|
||||
# create user for build
|
||||
RUN adduser --disabled-password lorang
|
||||
|
|
103
src/ast/ast.c
103
src/ast/ast.c
|
@ -19,8 +19,7 @@ struct AST_Node_t *AST_new_node(TokenLocation location, enum AST_SyntaxElement_t
|
|||
|
||||
// init to discrete state
|
||||
node->parent = NULL;
|
||||
node->children = NULL;
|
||||
node->child_count = 0;
|
||||
node->children = mem_new_g_array(MemoryNamespaceAst, sizeof(AST_NODE_PTR));
|
||||
node->kind = kind;
|
||||
node->value = value;
|
||||
node->location = location;
|
||||
|
@ -131,17 +130,6 @@ void AST_push_node(struct AST_Node_t *owner, struct AST_Node_t *child) {
|
|||
assert(owner != NULL);
|
||||
assert(child != NULL);
|
||||
|
||||
// if there are no children for now
|
||||
if (owner->child_count == 0) {
|
||||
DEBUG("Allocating new children array");
|
||||
owner->children = mem_alloc(MemoryNamespaceAst, sizeof(struct AST_Node_t *));
|
||||
|
||||
} else {
|
||||
DEBUG("Rellocating old children array");
|
||||
const size_t size = sizeof(struct AST_Node_t *) * (owner->child_count + 1);
|
||||
owner->children = mem_realloc(MemoryNamespaceAst, owner->children, size);
|
||||
}
|
||||
|
||||
if (owner->children == NULL) {
|
||||
PANIC("failed to allocate children array of AST node");
|
||||
}
|
||||
|
@ -152,22 +140,26 @@ void AST_push_node(struct AST_Node_t *owner, struct AST_Node_t *child) {
|
|||
owner->location.col_start = min(owner->location.col_start, child->location.col_start);
|
||||
owner->location.line_start = min(owner->location.line_start, child->location.line_start);
|
||||
|
||||
if (owner->location.file == NULL) {
|
||||
owner->location.file = child->location.file;
|
||||
}
|
||||
|
||||
assert(owner->children != NULL);
|
||||
|
||||
owner->children[owner->child_count++] = child;
|
||||
g_array_append_val(owner->children, child);
|
||||
}
|
||||
|
||||
struct AST_Node_t *AST_get_node(struct AST_Node_t *owner, const size_t idx) {
|
||||
DEBUG("retrvieng node %d from %p", idx, owner);
|
||||
assert(owner != NULL);
|
||||
assert(owner->children != NULL);
|
||||
assert(idx < owner->child_count);
|
||||
assert(idx < owner->children->len);
|
||||
|
||||
if (owner->children == NULL) {
|
||||
PANIC("AST owner node has no children");
|
||||
}
|
||||
|
||||
struct AST_Node_t *child = owner->children[idx];
|
||||
AST_NODE_PTR child = g_array_index(owner->children, AST_NODE_PTR, idx);
|
||||
|
||||
if (child == NULL) {
|
||||
PANIC("child node is NULL");
|
||||
|
@ -179,19 +171,13 @@ struct AST_Node_t *AST_get_node(struct AST_Node_t *owner, const size_t idx) {
|
|||
struct AST_Node_t* AST_remove_child(struct AST_Node_t* owner, const size_t idx) {
|
||||
assert(owner != NULL);
|
||||
assert(owner->children != NULL);
|
||||
assert(idx < owner->child_count);
|
||||
assert(idx < owner->children->len);
|
||||
|
||||
struct AST_Node_t* child = owner->children[idx];
|
||||
AST_NODE_PTR child = g_array_index(owner->children, AST_NODE_PTR, idx);
|
||||
g_array_remove_index(owner->children, idx);
|
||||
|
||||
child->parent = NULL;
|
||||
|
||||
owner->child_count--;
|
||||
|
||||
// shift back every following element by one
|
||||
for (size_t i = idx; i < owner->child_count; i++) {
|
||||
owner->children[i] = owner->children[i + 1];
|
||||
}
|
||||
|
||||
return child;
|
||||
}
|
||||
|
||||
|
@ -200,8 +186,8 @@ struct AST_Node_t* AST_detach_child(struct AST_Node_t* owner, const struct AST_N
|
|||
assert(child != NULL);
|
||||
assert(owner->children != NULL);
|
||||
|
||||
for (size_t i = 0; i < owner->child_count; i++) {
|
||||
if (owner->children[i] == child) {
|
||||
for (size_t i = 0; i < owner->children->len; i++) {
|
||||
if (g_array_index(owner->children, AST_NODE_PTR, i) == child) {
|
||||
return AST_remove_child(owner, i);
|
||||
}
|
||||
}
|
||||
|
@ -221,10 +207,10 @@ void AST_delete_node(struct AST_Node_t *node) {
|
|||
}
|
||||
|
||||
if (node->children != NULL) {
|
||||
for (size_t i = 0; i < node->child_count; i++) {
|
||||
for (size_t i = 0; i < AST_get_child_count(node); i++) {
|
||||
// prevent detach of children node
|
||||
node->children[i]->parent = NULL;
|
||||
AST_delete_node(node->children[i]);
|
||||
AST_get_node(node, i)->parent = NULL;
|
||||
AST_delete_node(AST_get_node(node, i));
|
||||
}
|
||||
mem_free(node->children);
|
||||
}
|
||||
|
@ -242,8 +228,8 @@ static void AST_visit_nodes_recurse2(struct AST_Node_t *root,
|
|||
|
||||
(for_each)(root, depth);
|
||||
|
||||
for (size_t i = 0; i < root->child_count; i++) {
|
||||
AST_visit_nodes_recurse2(root->children[i], for_each, depth + 1);
|
||||
for (size_t i = 0; i < root->children->len; i++) {
|
||||
AST_visit_nodes_recurse2(g_array_index(root->children, AST_NODE_PTR, i), for_each, depth + 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -270,8 +256,8 @@ static void AST_fprint_graphviz_node_definition(FILE* stream, const struct AST_N
|
|||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < node->child_count; i++) {
|
||||
AST_fprint_graphviz_node_definition(stream, node->children[i]);
|
||||
for (size_t i = 0; i < node->children->len; i++) {
|
||||
AST_fprint_graphviz_node_definition(stream, g_array_index(node->children, AST_NODE_PTR, i));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -285,9 +271,10 @@ static void AST_fprint_graphviz_node_connection(FILE* stream, const struct AST_N
|
|||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < node->child_count; i++) {
|
||||
fprintf(stream, "\tnode%p -- node%p\n", (void*) node, (void*) node->children[i]);
|
||||
AST_fprint_graphviz_node_connection(stream, node->children[i]);
|
||||
for (size_t i = 0; i < node->children->len; i++) {
|
||||
AST_NODE_PTR child = g_array_index(node->children, AST_NODE_PTR, i);
|
||||
fprintf(stream, "\tnode%p -- node%p\n", (void*) node, (void*) child);
|
||||
AST_fprint_graphviz_node_connection(stream, child);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -304,3 +291,45 @@ void AST_fprint_graphviz(FILE* stream, const struct AST_Node_t* root) {
|
|||
|
||||
fprintf(stream, "}\n");
|
||||
}
|
||||
|
||||
AST_NODE_PTR AST_get_node_by_kind(AST_NODE_PTR owner, enum AST_SyntaxElement_t kind) {
|
||||
for (size_t i = 0; i < owner->children->len; i++) {
|
||||
AST_NODE_PTR child = AST_get_node(owner, i);
|
||||
|
||||
if (child->kind == kind) {
|
||||
return child;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void AST_merge_modules(AST_NODE_PTR dst, size_t k, AST_NODE_PTR src) {
|
||||
assert(dst != NULL);
|
||||
assert(src != NULL);
|
||||
|
||||
size_t elements = src->children->len;
|
||||
for (size_t i = 0; i < elements; i++) {
|
||||
AST_insert_node(dst, k + i, AST_remove_child(src, 0));
|
||||
}
|
||||
AST_delete_node(src);
|
||||
}
|
||||
|
||||
void AST_insert_node(AST_NODE_PTR owner, size_t idx, AST_NODE_PTR child) {
|
||||
assert(owner != NULL);
|
||||
assert(child != NULL);
|
||||
|
||||
DEBUG("Reallocating old children array");
|
||||
|
||||
g_array_insert_val(owner->children, idx, child);
|
||||
}
|
||||
|
||||
size_t AST_get_child_count(AST_NODE_PTR node) {
|
||||
return node->children->len;
|
||||
}
|
||||
|
||||
AST_NODE_PTR AST_get_last_node(AST_NODE_PTR node) {
|
||||
assert(node != NULL);
|
||||
|
||||
return g_array_index(node->children, AST_NODE_PTR, node->children->len - 1);
|
||||
}
|
||||
|
|
|
@ -89,7 +89,7 @@ enum AST_SyntaxElement_t {
|
|||
* - kind: The type of the node. Such as AST_Expr, AST_Add, ...
|
||||
* - value: A string representing an optional value. Can be a integer literal for kind AST_int
|
||||
*/
|
||||
struct AST_Node_t {
|
||||
typedef struct AST_Node_t {
|
||||
// parent node that owns this node
|
||||
struct AST_Node_t *parent;
|
||||
|
||||
|
@ -100,12 +100,9 @@ struct AST_Node_t {
|
|||
|
||||
TokenLocation location;
|
||||
|
||||
// number of child nodes ownd by this node
|
||||
// length of children array
|
||||
size_t child_count;
|
||||
// variable amount of child nodes
|
||||
struct AST_Node_t **children;
|
||||
};
|
||||
// children array
|
||||
GArray* children;
|
||||
} AST_Node;
|
||||
|
||||
/**
|
||||
* Shorthand type for a single AST node
|
||||
|
@ -199,6 +196,8 @@ struct AST_Node_t* AST_detach_child(struct AST_Node_t* owner, const struct AST_N
|
|||
[[gnu::nonnull(1)]]
|
||||
struct AST_Node_t *AST_get_node(struct AST_Node_t *owner, size_t idx);
|
||||
|
||||
AST_NODE_PTR AST_get_last_node(AST_NODE_PTR node);
|
||||
|
||||
/**
|
||||
* @brief Execute a function for every child, grandchild, ... and the supplied node as topmost ancestor
|
||||
* @param root the root to recursively execute a function for
|
||||
|
@ -219,4 +218,14 @@ void AST_visit_nodes_recurse(struct AST_Node_t *root,
|
|||
[[gnu::nonnull(1), gnu::nonnull(2)]]
|
||||
void AST_fprint_graphviz(FILE* stream, const struct AST_Node_t* node);
|
||||
|
||||
AST_NODE_PTR AST_get_node_by_kind(AST_NODE_PTR owner, enum AST_SyntaxElement_t kind);
|
||||
|
||||
[[gnu::nonnull(1), gnu::nonnull(3)]]
|
||||
void AST_merge_modules(AST_NODE_PTR dst, size_t i, AST_NODE_PTR src);
|
||||
|
||||
[[gnu::nonnull(1), gnu::nonnull(3)]]
|
||||
void AST_insert_node(AST_NODE_PTR owner, size_t idx, AST_NODE_PTR child);
|
||||
|
||||
size_t AST_get_child_count(AST_NODE_PTR node);
|
||||
|
||||
#endif
|
|
@ -103,6 +103,10 @@ TargetConfig* default_target_config() {
|
|||
config->output_directory = mem_strdup(MemoryNamespaceOpt, "bin");
|
||||
config->optimization_level = 1;
|
||||
config->root_module = NULL;
|
||||
config->link_search_paths = g_array_new(FALSE, FALSE, sizeof(char*));
|
||||
config->lld_fatal_warnings = FALSE;
|
||||
config->gsc_fatal_warnings = FALSE;
|
||||
config->import_paths = mem_new_g_array(MemoryNamespaceOpt, sizeof(char*));
|
||||
|
||||
return config;
|
||||
}
|
||||
|
@ -112,6 +116,16 @@ TargetConfig* default_target_config_from_args() {
|
|||
|
||||
TargetConfig* config = default_target_config();
|
||||
|
||||
gboolean fatal_warnings = is_option_set("all-fatal-warnings");
|
||||
|
||||
if (fatal_warnings || is_option_set("lld-fatal-warnings")) {
|
||||
config->lld_fatal_warnings = true;
|
||||
}
|
||||
|
||||
if (fatal_warnings || is_option_set("gsc-fatal-warnings")) {
|
||||
config->gsc_fatal_warnings = true;
|
||||
}
|
||||
|
||||
if (is_option_set("print-ast")) {
|
||||
config->print_ast = true;
|
||||
}
|
||||
|
@ -146,6 +160,40 @@ TargetConfig* default_target_config_from_args() {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: free vvvvvvvvvvvvv
|
||||
char* cwd = g_get_current_dir();
|
||||
g_array_append_val(config->link_search_paths, cwd);
|
||||
|
||||
if (is_option_set("link-paths")) {
|
||||
const Option* opt = get_option("link-paths");
|
||||
|
||||
if (opt->value != NULL) {
|
||||
|
||||
const char* start = opt->value;
|
||||
const char* end = NULL;
|
||||
while((end = strchr(start, ',')) != NULL) {
|
||||
|
||||
const int len = end - start;
|
||||
char* link_path = malloc(len + 1);
|
||||
memcpy(link_path, start, len);
|
||||
link_path[len] = 0;
|
||||
|
||||
g_array_append_val(config->link_search_paths, link_path);
|
||||
|
||||
start = end;
|
||||
}
|
||||
|
||||
const int len = strlen(start);
|
||||
if (len > 0) {
|
||||
char* link_path = malloc(len + 1);
|
||||
memcpy(link_path, start, len);
|
||||
link_path[len] = 0;
|
||||
|
||||
g_array_append_val(config->link_search_paths, link_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GArray* files = get_non_options_after("compile");
|
||||
|
||||
if (files == NULL) {
|
||||
|
@ -161,6 +209,9 @@ TargetConfig* default_target_config_from_args() {
|
|||
g_array_free(files, TRUE);
|
||||
}
|
||||
|
||||
char* default_import_path = mem_strdup(MemoryNamespaceOpt, ".");
|
||||
g_array_append_val(config->import_paths, default_import_path);
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
|
@ -173,17 +224,23 @@ void print_help(void) {
|
|||
"Compile non-project file: gsc compile <target-options> [file]",
|
||||
"Output information: gsc <option>",
|
||||
"Target options:",
|
||||
" --print-ast print resulting abstract syntax tree to a file",
|
||||
" --print-asm print resulting assembly language to a file",
|
||||
" --print-ir print resulting LLVM-IR to a file",
|
||||
" --mode=[app|lib] set the compilation mode to either application or library",
|
||||
" --output=name name of output files without extension",
|
||||
" --print-ast print resulting abstract syntax tree to a file",
|
||||
" --print-asm print resulting assembly language to a file",
|
||||
" --print-ir print resulting LLVM-IR to a file",
|
||||
" --mode=[app|lib] set the compilation mode to either application or library",
|
||||
" --output=name name of output files without extension",
|
||||
" --link-paths=[paths,] set a list of directories to for libraries in",
|
||||
" --all-fatal-warnings treat all warnings as errors",
|
||||
" --lld-fatal-warnings treat linker warnings as errors",
|
||||
" --gsc-fatal-warnings treat parser warnings as errors",
|
||||
"Options:",
|
||||
" --verbose print logs with level information or higher",
|
||||
" --debug print debug logs (if not disabled at compile time)",
|
||||
" --version print the version",
|
||||
" --help print this hel dialog",
|
||||
" --print-memory-stats print statistics of the garbage collector"
|
||||
" --verbose print logs with level information or higher",
|
||||
" --debug print debug logs (if not disabled at compile time)",
|
||||
" --version print the version",
|
||||
" --list-targets print a list of all available targets supported",
|
||||
" --help print this help dialog",
|
||||
" --color-always always colorize output",
|
||||
" --print-gc-stats print statistics of the garbage collector"
|
||||
};
|
||||
|
||||
for (unsigned int i = 0; i < sizeof(lines) / sizeof(const char *); i++) {
|
||||
|
@ -289,6 +346,8 @@ static int parse_target(const ProjectConfig *config, const toml_table_t *target_
|
|||
get_str(&target_config->root_module, target_table, "root");
|
||||
get_str(&target_config->output_directory, target_table, "output");
|
||||
get_str(&target_config->archive_directory, target_table, "archive");
|
||||
get_bool(&target_config->lld_fatal_warnings, target_table, "lld_fatal_warnings");
|
||||
get_bool(&target_config->gsc_fatal_warnings, target_table, "gsc_fatal_warnings");
|
||||
|
||||
get_int(&target_config->optimization_level, target_table, "opt");
|
||||
|
||||
|
@ -374,6 +433,12 @@ void delete_target_config(TargetConfig* config) {
|
|||
if (config->output_directory != NULL) {
|
||||
mem_free(config->output_directory);
|
||||
}
|
||||
if (config->link_search_paths) {
|
||||
for (guint i = 0; i < config->link_search_paths->len; i++) {
|
||||
free(g_array_index(config->link_search_paths, char*, i));
|
||||
}
|
||||
g_array_free(config->link_search_paths, TRUE);
|
||||
}
|
||||
mem_free(config);
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,16 @@
|
|||
|
||||
#define TOML_ERROR_MSG_BUF 256
|
||||
|
||||
typedef struct TargetLinkConfig_t {
|
||||
// name of object files to link
|
||||
GArray* object_file_names;
|
||||
// treat warnings as errors
|
||||
gboolean fatal_warnings;
|
||||
// colorize linker output
|
||||
bool colorize;
|
||||
char* output_file;
|
||||
} TargetLinkConfig;
|
||||
|
||||
typedef enum TargetCompilationMode_t {
|
||||
// output an executable binary
|
||||
Application,
|
||||
|
@ -44,6 +54,14 @@ typedef struct TargetConfig_t {
|
|||
TargetCompilationMode mode;
|
||||
// number between 1 and 3
|
||||
int optimization_level;
|
||||
// path to look for object files
|
||||
// (can be extra library paths, auto included is output_directory)
|
||||
GArray* link_search_paths;
|
||||
// treat linker warnings as errors
|
||||
bool lld_fatal_warnings;
|
||||
// treat parser warnings as errors
|
||||
bool gsc_fatal_warnings;
|
||||
GArray* import_paths;
|
||||
} TargetConfig;
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
|
||||
#include "set/types.h"
|
||||
#include <codegen/backend.h>
|
||||
#include <sys/log.h>
|
||||
|
||||
static struct CodegenBackend_t {
|
||||
codegen_init init_func;
|
||||
codegen_deinit deinit_func;
|
||||
codegen codegen_func;
|
||||
const char* name;
|
||||
} CodegenBackend;
|
||||
|
||||
BackendError new_backend_error(BackendErrorKind kind) {
|
||||
BackendError error;
|
||||
error.kind = kind;
|
||||
error.impl.ast_node = NULL;
|
||||
error.impl.message = NULL;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
BackendError new_backend_impl_error(BackendErrorKind kind, AST_NODE_PTR node, const char* message) {
|
||||
BackendError error;
|
||||
error.kind = kind;
|
||||
error.impl.ast_node = node;
|
||||
error.impl.message = message;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
BackendError init_backend(void) {
|
||||
DEBUG("initializing backend: %s", CodegenBackend.name);
|
||||
|
||||
if (CodegenBackend.init_func == NULL) {
|
||||
ERROR("backend: %s is not properly initialized", CodegenBackend.name);
|
||||
return new_backend_error(NoBackend);
|
||||
}
|
||||
|
||||
BackendError code = CodegenBackend.init_func();
|
||||
|
||||
if (code.kind != Success) {
|
||||
ERROR("failed to initialize backend: %s with code: %ld", CodegenBackend.name, code);
|
||||
return code;
|
||||
}
|
||||
|
||||
return new_backend_error(Success);
|
||||
}
|
||||
|
||||
BackendError deinit_backend(void) {
|
||||
DEBUG("undoing initializing of backend: %s", CodegenBackend.name);
|
||||
|
||||
if (CodegenBackend.deinit_func == NULL) {
|
||||
ERROR("backend: %s is not properly initialized", CodegenBackend.name);
|
||||
return new_backend_error(NoBackend);
|
||||
}
|
||||
|
||||
BackendError code = CodegenBackend.deinit_func();
|
||||
|
||||
if (code.kind != Success) {
|
||||
ERROR("failed to undo initialization of backend: %s with code: %ld", CodegenBackend.name, code);
|
||||
return code;
|
||||
}
|
||||
|
||||
return new_backend_error(Success);
|
||||
}
|
||||
|
||||
BackendError set_backend(const codegen_init init_func, const codegen_deinit deinit_func, const codegen codegen_func, const char* name) {
|
||||
CodegenBackend.init_func = init_func;
|
||||
CodegenBackend.deinit_func = deinit_func;
|
||||
CodegenBackend.codegen_func = codegen_func;
|
||||
CodegenBackend.name = name;
|
||||
|
||||
return new_backend_error(Success);
|
||||
}
|
||||
|
||||
BackendError generate_code(const Module* root, const TargetConfig* target) {
|
||||
DEBUG("generating code with backend: %s", CodegenBackend.name);
|
||||
|
||||
if (CodegenBackend.codegen_func == NULL) {
|
||||
ERROR("backend: %s is not properly initialized", CodegenBackend.name);
|
||||
return new_backend_error(NoBackend);
|
||||
}
|
||||
|
||||
BackendError code = CodegenBackend.codegen_func(root, target);
|
||||
if (code.kind) {
|
||||
ERROR("code generation of backend: %s failed with code: %ld `%s`", CodegenBackend.name, code, code.impl.message);
|
||||
return code;
|
||||
}
|
||||
|
||||
return new_backend_error(Success);
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
|
||||
#ifndef CODEGN_BACKEND_H_
|
||||
#define CODEGN_BACKEND_H_
|
||||
|
||||
#include <set/types.h>
|
||||
#include <ast/ast.h>
|
||||
#include <cfg/opt.h>
|
||||
|
||||
typedef struct BackendImplError_t {
|
||||
// faulty AST node
|
||||
AST_NODE_PTR ast_node;
|
||||
// error message
|
||||
const char* message;
|
||||
} BackendImplError;
|
||||
|
||||
typedef enum BackendErrorKind_t {
|
||||
Success,
|
||||
NoBackend,
|
||||
BackendAlreadySet,
|
||||
Unimplemented,
|
||||
// some error occurred in the backend implementation
|
||||
Implementation
|
||||
} BackendErrorKind;
|
||||
|
||||
typedef struct BackendError_t {
|
||||
BackendErrorKind kind;
|
||||
BackendImplError impl;
|
||||
} BackendError;
|
||||
|
||||
/**
|
||||
* @brief Function called by the compiler backend to generate an intermediate format
|
||||
* from AST. Returns a custom container for its intermediate language.
|
||||
*/
|
||||
typedef BackendError (*codegen)(const Module*, const TargetConfig* target);
|
||||
|
||||
/**
|
||||
* @brief Initialize the code generation backend.
|
||||
*/
|
||||
typedef BackendError (*codegen_init)(void);
|
||||
|
||||
/**
|
||||
* @brief Undo initialization of code generation backend.
|
||||
*/
|
||||
typedef BackendError (*codegen_deinit)(void);
|
||||
|
||||
/**
|
||||
* @brief Set the backend functions to use
|
||||
*
|
||||
* @param init_func the function to call for initializing the backend
|
||||
* @param deinit_func the function to call for undoing the initialization of the backend
|
||||
* @param codegen_func the function to call when generating code
|
||||
* @param name name of the backend
|
||||
*/
|
||||
[[nodiscard]]
|
||||
[[gnu::nonnull(1), gnu::nonnull(2), gnu::nonnull(3), gnu::nonnull(3)]]
|
||||
BackendError set_backend(const codegen_init init_func, const codegen_deinit deinit_func, const codegen codegen_func, const char* name);
|
||||
|
||||
/**
|
||||
* @brief Call the initialization function of the backend
|
||||
*
|
||||
* @return BackendError
|
||||
*/
|
||||
[[nodiscard]]
|
||||
BackendError init_backend(void);
|
||||
|
||||
/**
|
||||
* @brief Call the undo initialization function of the backend
|
||||
*
|
||||
* @return BackendError
|
||||
*/
|
||||
[[nodiscard]]
|
||||
BackendError deinit_backend(void);
|
||||
|
||||
/**
|
||||
* @brief Generate intermediate code with the registered backend
|
||||
*
|
||||
* @param root the root node of the AST
|
||||
* @param code output pointer to the intermediate code
|
||||
* @return BackendError
|
||||
*/
|
||||
[[nodiscard]]
|
||||
BackendError generate_code(const Module* root, const TargetConfig* target);
|
||||
|
||||
/**
|
||||
* @brief Create a new backend error
|
||||
*
|
||||
* @param kind must ne != Implementation
|
||||
* @return BackendError
|
||||
*/
|
||||
BackendError new_backend_error(BackendErrorKind kind);
|
||||
|
||||
BackendError new_backend_impl_error(BackendErrorKind kind, AST_NODE_PTR node, const char* message);
|
||||
|
||||
#define SUCCESS new_backend_error(Success)
|
||||
|
||||
#endif // CODEGN_BACKEND_H_
|
151
src/compiler.c
151
src/compiler.c
|
@ -11,7 +11,12 @@
|
|||
#include <io/files.h>
|
||||
#include <assert.h>
|
||||
#include <cfg/opt.h>
|
||||
#include <codegen/backend.h>
|
||||
#include <llvm/backend.h>
|
||||
#include <mem/cache.h>
|
||||
#include <set/set.h>
|
||||
|
||||
#define GRAPHVIZ_FILE_EXTENSION "gv"
|
||||
|
||||
extern void yyrestart(FILE *);
|
||||
|
||||
|
@ -52,14 +57,14 @@ static int compile_file_to_ast(AST_NODE_PTR ast, ModuleFile *file) {
|
|||
yyrestart(yyin);
|
||||
lex_reset();
|
||||
|
||||
yyparse();
|
||||
int status = yyparse();
|
||||
|
||||
// clean up global state
|
||||
// current_file = NULL;
|
||||
root = NULL;
|
||||
yyin = NULL;
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -93,6 +98,8 @@ static int setup_target_environment(const TargetConfig *target) {
|
|||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
INFO("setup environment successfully");
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -101,29 +108,134 @@ static int setup_target_environment(const TargetConfig *target) {
|
|||
* @param ast
|
||||
* @param target
|
||||
*/
|
||||
static void print_ast_to_file(const AST_NODE_PTR ast, const TargetConfig *target) {
|
||||
static void print_ast_to_file(AST_NODE_PTR ast, const TargetConfig *target) {
|
||||
assert(ast != NULL);
|
||||
assert(target != NULL);
|
||||
DEBUG("printing AST to file: %s", target->name);
|
||||
|
||||
if (!target->print_ast)
|
||||
if (!target->print_ast) {
|
||||
INFO("no need to print AST");
|
||||
return;
|
||||
}
|
||||
|
||||
// create file path to write graphviz to
|
||||
const char *path = make_file_path(target->name, ".gv", 1, target->archive_directory);
|
||||
// basename of ile
|
||||
char* filename = g_strjoin(".", target->name, GRAPHVIZ_FILE_EXTENSION, NULL);
|
||||
// relative path to file
|
||||
char *path = g_build_filename(target->archive_directory, filename, NULL);
|
||||
|
||||
FILE *output = fopen((const char *) path, "w");
|
||||
DEBUG("Opening file to graph: %s", path);
|
||||
|
||||
FILE *output = fopen(path, "w");
|
||||
if (output == NULL) {
|
||||
const char *message = get_last_error();
|
||||
char *message = (char*) get_last_error();
|
||||
print_message(Error, "Unable to open file for syntax tree at: %s: %s", path, message);
|
||||
free((void *) message);
|
||||
free(message);
|
||||
} else {
|
||||
DEBUG("writing graph to file...");
|
||||
|
||||
AST_fprint_graphviz(output, ast);
|
||||
|
||||
fclose(output);
|
||||
|
||||
print_message(Info, "AST graph was written to: %s", path);
|
||||
}
|
||||
|
||||
free((void *) path);
|
||||
g_free(filename);
|
||||
g_free(path);
|
||||
}
|
||||
|
||||
static void run_backend_codegen(const Module* module, const TargetConfig* target) {
|
||||
DEBUG("initializing LLVM codegen backend...");
|
||||
llvm_backend_init();
|
||||
|
||||
DEBUG("initiializing backend for codegen...");
|
||||
BackendError err = init_backend();
|
||||
if (err.kind != Success) {
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG("generating code...");
|
||||
err = generate_code(module, target);
|
||||
if (err.kind != Success) {
|
||||
print_message(Error, "Backend failed: %s", err.impl.message);
|
||||
return;
|
||||
}
|
||||
|
||||
print_message(Info, "Compilation finished successfully");
|
||||
|
||||
err = deinit_backend();
|
||||
}
|
||||
|
||||
const char* get_absolute_import_path(const TargetConfig* config, const char* import_target_name) {
|
||||
INFO("resolving absolute path for import target: %s", import_target_name);
|
||||
|
||||
for (guint i = 0; i < config->import_paths->len; i++) {
|
||||
const char* import_directory_path = g_array_index(config->import_paths, char*, i);
|
||||
|
||||
char* path = g_build_filename(import_directory_path, import_target_name, NULL);
|
||||
char* cwd = g_get_current_dir();
|
||||
char* canonical = g_canonicalize_filename(path, cwd);
|
||||
|
||||
const gboolean exists = g_file_test(canonical, G_FILE_TEST_EXISTS);
|
||||
const gboolean is_dir = g_file_test(canonical, G_FILE_TEST_IS_DIR);
|
||||
|
||||
g_free(path);
|
||||
g_free(cwd);
|
||||
|
||||
if (exists && !is_dir) {
|
||||
INFO("import target found at: %s", canonical);
|
||||
return canonical;
|
||||
}
|
||||
|
||||
g_free(canonical);
|
||||
}
|
||||
|
||||
// file not found
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int compile_module_with_dependencies(ModuleFileStack *unit, ModuleFile* file, const TargetConfig *target, AST_NODE_PTR root_module) {
|
||||
|
||||
GHashTable* imports = mem_new_g_hash_table(MemoryNamespaceAst, g_str_hash, g_str_equal);
|
||||
|
||||
if (compile_file_to_ast(root_module, file) == EXIT_SUCCESS) {
|
||||
|
||||
for (size_t i = 0; i < AST_get_child_count(root_module); i++) {
|
||||
AST_NODE_PTR child = AST_get_node(root_module, i);
|
||||
|
||||
if (child->kind == AST_Import) {
|
||||
|
||||
const char* path = get_absolute_import_path(target, child->value);
|
||||
if (path == NULL) {
|
||||
print_message(Error, "Cannot resolve path for import: `%s`", child->value);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (g_hash_table_contains(imports, path)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ModuleFile *imported_file = push_file(unit, path);
|
||||
AST_NODE_PTR imported_module = AST_new_node(empty_location(imported_file), AST_Module, NULL);
|
||||
|
||||
if (compile_file_to_ast(imported_module, imported_file) == EXIT_SUCCESS) {
|
||||
AST_merge_modules(root_module, i + 1, imported_module);
|
||||
} else {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
g_hash_table_insert(imports, (gpointer) path, NULL);
|
||||
|
||||
gchar* directory = g_path_get_dirname(path);
|
||||
g_array_append_val(target->import_paths, directory);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -134,23 +246,28 @@ static void print_ast_to_file(const AST_NODE_PTR ast, const TargetConfig *target
|
|||
static void build_target(ModuleFileStack *unit, const TargetConfig *target) {
|
||||
print_message(Info, "Building target: %s", target->name);
|
||||
|
||||
AST_NODE_PTR ast = AST_new_node(empty_location(), AST_Module, NULL);
|
||||
ModuleFile *file = push_file(unit, target->root_module);
|
||||
AST_NODE_PTR root_module = AST_new_node(empty_location(file), AST_Module, NULL);
|
||||
|
||||
if (compile_file_to_ast(ast, file) == EXIT_SUCCESS) {
|
||||
if (setup_target_environment(target) == 0) {
|
||||
if (compile_module_with_dependencies(unit, file, target, root_module) == EXIT_SUCCESS) {
|
||||
if (root_module != NULL) {
|
||||
if (setup_target_environment(target) == 0) {
|
||||
|
||||
print_ast_to_file(ast, target);
|
||||
print_ast_to_file(root_module, target);
|
||||
Module *module = create_set(root_module);
|
||||
|
||||
// TODO: parse AST to semantic values
|
||||
// TODO: backend codegen
|
||||
if (module != NULL) {
|
||||
run_backend_codegen(module, target);
|
||||
}
|
||||
}
|
||||
|
||||
AST_delete_node(root_module);
|
||||
}
|
||||
}
|
||||
|
||||
AST_delete_node(ast);
|
||||
|
||||
mem_purge_namespace(MemoryNamespaceLex);
|
||||
mem_purge_namespace(MemoryNamespaceAst);
|
||||
mem_purge_namespace(MemoryNamespaceSet);
|
||||
|
||||
print_file_statistics(file);
|
||||
}
|
||||
|
|
118
src/io/files.c
118
src/io/files.c
|
@ -3,6 +3,8 @@
|
|||
//
|
||||
|
||||
#include <io/files.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/log.h>
|
||||
#include <assert.h>
|
||||
#include <sys/col.h>
|
||||
|
@ -40,22 +42,24 @@ ModuleFile *push_file(ModuleFileStack *stack, const char *path) {
|
|||
|
||||
// lazy init of heap stack
|
||||
if (stack->files == NULL) {
|
||||
stack->files = g_array_new(FALSE, FALSE, sizeof(ModuleFile));
|
||||
stack->files = g_array_new(FALSE, FALSE, sizeof(ModuleFile*));
|
||||
}
|
||||
|
||||
ModuleFile new_file = {
|
||||
.path = path,
|
||||
.handle = NULL
|
||||
};
|
||||
ModuleFile* new_file = mem_alloc(MemoryNamespaceStatic, sizeof(ModuleFile));
|
||||
new_file->handle = NULL;
|
||||
new_file->path = path;
|
||||
new_file->statistics.warning_count = 0;
|
||||
new_file->statistics.error_count = 0;
|
||||
new_file->statistics.info_count = 0;
|
||||
|
||||
g_array_append_val(stack->files, new_file);
|
||||
|
||||
return ((ModuleFile *) stack->files->data) + stack->files->len - 1;
|
||||
return new_file;
|
||||
}
|
||||
|
||||
void delete_files(ModuleFileStack *stack) {
|
||||
for (size_t i = 0; i < stack->files->len; i++) {
|
||||
const ModuleFile *file = (ModuleFile *) stack->files->data + i;
|
||||
const ModuleFile *file = g_array_index(stack->files, ModuleFile*, i);
|
||||
|
||||
if (file->handle != NULL) {
|
||||
DEBUG("closing file: %s", file->path);
|
||||
|
@ -74,6 +78,11 @@ void delete_files(ModuleFileStack *stack) {
|
|||
|
||||
// behaves like fgets except that it has defined behavior when n == 1
|
||||
static void custom_fgets(char *buffer, size_t n, FILE *stream) {
|
||||
if (feof(stream)) {
|
||||
buffer[0] = '\n';
|
||||
buffer[1] = 0;
|
||||
return;
|
||||
}
|
||||
if (n == 1) {
|
||||
buffer[0] = (char) fgetc(stream);
|
||||
buffer[1] = 0;
|
||||
|
@ -82,19 +91,20 @@ static void custom_fgets(char *buffer, size_t n, FILE *stream) {
|
|||
}
|
||||
}
|
||||
|
||||
void print_diagnostic(ModuleFile *file, TokenLocation *location, Message kind, const char *message) {
|
||||
assert(file->handle != NULL);
|
||||
void print_diagnostic(TokenLocation *location, Message kind, const char *message, ...) {
|
||||
assert(location->file != NULL);
|
||||
assert(location->file->handle != NULL);
|
||||
assert(location != NULL);
|
||||
assert(message != NULL);
|
||||
|
||||
// reset to start
|
||||
rewind(file->handle);
|
||||
rewind(location->file->handle);
|
||||
|
||||
char *buffer = alloca(SEEK_BUF_BYTES);
|
||||
unsigned long int line_count = 1;
|
||||
|
||||
// seek to first line
|
||||
while (line_count < location->line_start && fgets(buffer, SEEK_BUF_BYTES, file->handle) != NULL) {
|
||||
while (line_count < location->line_start && fgets(buffer, SEEK_BUF_BYTES, location->file->handle) != NULL) {
|
||||
line_count += strchr(buffer, '\n') != NULL;
|
||||
}
|
||||
|
||||
|
@ -104,24 +114,32 @@ void print_diagnostic(ModuleFile *file, TokenLocation *location, Message kind, c
|
|||
case Info:
|
||||
kind_text = "info";
|
||||
accent_color = CYAN;
|
||||
file->statistics.info_count++;
|
||||
location->file->statistics.info_count++;
|
||||
break;
|
||||
case Warning:
|
||||
kind_text = "warning";
|
||||
accent_color = YELLOW;
|
||||
file->statistics.warning_count++;
|
||||
location->file->statistics.warning_count++;
|
||||
break;
|
||||
case Error:
|
||||
kind_text = "error";
|
||||
accent_color = RED;
|
||||
file->statistics.error_count++;
|
||||
location->file->statistics.error_count++;
|
||||
break;
|
||||
}
|
||||
|
||||
const char *absolute_path = get_absolute_path(file->path);
|
||||
const char *absolute_path = get_absolute_path(location->file->path);
|
||||
|
||||
printf("%s%s:%ld:%s %s%s:%s %s\n", BOLD, absolute_path, location->line_start, RESET, accent_color, kind_text, RESET,
|
||||
message);
|
||||
printf("%s%s:%ld:%s %s%s:%s ", BOLD, absolute_path, location->line_start, RESET, accent_color, kind_text, RESET);
|
||||
|
||||
va_list args;
|
||||
va_start(args, message);
|
||||
|
||||
vprintf(message, args);
|
||||
|
||||
va_end(args);
|
||||
|
||||
printf("\n");
|
||||
|
||||
mem_free((void *) absolute_path);
|
||||
|
||||
|
@ -135,7 +153,7 @@ void print_diagnostic(ModuleFile *file, TokenLocation *location, Message kind, c
|
|||
// print line before token group start
|
||||
unsigned long int limit = min(location->col_start, SEEK_BUF_BYTES);
|
||||
while (limit > 1) {
|
||||
custom_fgets(buffer, (int) limit, file->handle);
|
||||
custom_fgets(buffer, (int) limit, location->file->handle);
|
||||
chars += printf("%s", buffer);
|
||||
limit = min(location->col_start - chars, SEEK_BUF_BYTES);
|
||||
|
||||
|
@ -149,7 +167,7 @@ void print_diagnostic(ModuleFile *file, TokenLocation *location, Message kind, c
|
|||
chars = 0;
|
||||
limit = min(location->col_end - location->col_start + 1, SEEK_BUF_BYTES);
|
||||
while (limit > 0) {
|
||||
custom_fgets(buffer, (int) limit, file->handle);
|
||||
custom_fgets(buffer, (int) limit, location->file->handle);
|
||||
chars += printf("%s", buffer);
|
||||
limit = min(location->col_end - location->col_start + 1 - chars, SEEK_BUF_BYTES);
|
||||
|
||||
|
@ -162,7 +180,7 @@ void print_diagnostic(ModuleFile *file, TokenLocation *location, Message kind, c
|
|||
|
||||
// print rest of the line
|
||||
do {
|
||||
custom_fgets(buffer, SEEK_BUF_BYTES, file->handle);
|
||||
custom_fgets(buffer, SEEK_BUF_BYTES, location->file->handle);
|
||||
printf("%s", buffer);
|
||||
} while (strchr(buffer, '\n') == NULL);
|
||||
|
||||
|
@ -185,24 +203,26 @@ void print_diagnostic(ModuleFile *file, TokenLocation *location, Message kind, c
|
|||
}
|
||||
|
||||
TokenLocation new_location(unsigned long int line_start, unsigned long int col_start, unsigned long int line_end,
|
||||
unsigned long int col_end) {
|
||||
unsigned long int col_end, ModuleFile* file) {
|
||||
TokenLocation location;
|
||||
|
||||
location.line_start = line_start;
|
||||
location.line_end = line_end;
|
||||
location.col_start = col_start;
|
||||
location.col_end = col_end;
|
||||
location.file = file;
|
||||
|
||||
return location;
|
||||
}
|
||||
|
||||
TokenLocation empty_location(void) {
|
||||
TokenLocation empty_location(ModuleFile* file) {
|
||||
TokenLocation location;
|
||||
|
||||
location.line_start = 0;
|
||||
location.line_end = 0;
|
||||
location.col_start = 0;
|
||||
location.col_end = 0;
|
||||
location.file = file;
|
||||
|
||||
return location;
|
||||
}
|
||||
|
@ -236,7 +256,7 @@ void print_unit_statistics(ModuleFileStack *file_stack) {
|
|||
stats.error_count = 0;
|
||||
|
||||
for (size_t i = 0; i < file_stack->files->len; i++) {
|
||||
ModuleFile *file = (ModuleFile *) file_stack->files->data;
|
||||
ModuleFile* file = g_array_index(file_stack->files, ModuleFile*, i);
|
||||
|
||||
stats.info_count += file->statistics.warning_count;
|
||||
stats.warning_count += file->statistics.warning_count;
|
||||
|
@ -297,14 +317,7 @@ int create_directory(const char *path) {
|
|||
|
||||
DEBUG("creating directory: %s", path);
|
||||
|
||||
int result;
|
||||
#ifdef __unix__
|
||||
result = mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
|
||||
#elif defined(_WIN32) || defined(WIN32)
|
||||
result = _mkdir(path);
|
||||
#endif
|
||||
|
||||
return result;
|
||||
return g_mkdir_with_parents(path, 0755);
|
||||
}
|
||||
|
||||
const char *get_last_error() {
|
||||
|
@ -316,44 +329,9 @@ const char *get_absolute_path(const char *path) {
|
|||
|
||||
DEBUG("resolving absolute path of: %s", path);
|
||||
|
||||
#ifdef __unix__
|
||||
// use unix specific function
|
||||
char absolute_path[MAX_PATH_BYTES];
|
||||
realpath(path, absolute_path);
|
||||
#elif defined(_WIN32) || defined(WIN32)
|
||||
// use Windows CRT specific function
|
||||
char absolute_path[MAX_PATH_BYTES];
|
||||
_fullpath((char*) path, absolute_path, _MAX_PATH);
|
||||
#endif
|
||||
char* cwd = g_get_current_dir();
|
||||
char* canoical = g_canonicalize_filename(path, cwd);
|
||||
g_free(cwd);
|
||||
|
||||
return mem_strdup(MemoryNamespaceIo, absolute_path);
|
||||
}
|
||||
|
||||
const char* make_file_path(const char* name, const char* ext, int count, ...) {
|
||||
DEBUG("building file path...");
|
||||
|
||||
va_list args;
|
||||
va_start(args, count); // Initialize the va_list with the first variadic argument
|
||||
|
||||
char* path = calloc(MAX_PATH_BYTES, sizeof(char));
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
const char* arg = va_arg(args, const char*);
|
||||
assert(arg != NULL);
|
||||
|
||||
strcat(path, arg);
|
||||
strcat(path, PATH_SEPARATOR);
|
||||
}
|
||||
|
||||
va_end(args); // Clean up the va_list
|
||||
|
||||
if (name != NULL) {
|
||||
strcat(path, name);
|
||||
}
|
||||
|
||||
if (name != NULL) {
|
||||
strcat(path, ext);
|
||||
}
|
||||
|
||||
return path;
|
||||
return canoical;
|
||||
}
|
||||
|
|
|
@ -41,6 +41,7 @@ typedef struct TokenLocation_t {
|
|||
unsigned long int col_start;
|
||||
unsigned long int line_end;
|
||||
unsigned long int col_end;
|
||||
ModuleFile* file;
|
||||
} TokenLocation;
|
||||
|
||||
/**
|
||||
|
@ -75,24 +76,23 @@ void delete_files(ModuleFileStack *stack);
|
|||
* @return
|
||||
*/
|
||||
TokenLocation new_location(unsigned long int line_start, unsigned long int col_start, unsigned long int line_end,
|
||||
unsigned long int col_end);
|
||||
unsigned long int col_end, ModuleFile* file);
|
||||
|
||||
/**
|
||||
* @brief Create a new empty location with all of its contents set to zero
|
||||
* @return
|
||||
*/
|
||||
TokenLocation empty_location(void);
|
||||
TokenLocation empty_location(ModuleFile* file);
|
||||
|
||||
/**
|
||||
* @brief Prints some diagnostic message to stdout.
|
||||
* This also print the token group and the attached source as context.
|
||||
* @param file
|
||||
* @param location
|
||||
* @param kind
|
||||
* @param message
|
||||
*/
|
||||
[[gnu::nonnull(1), gnu::nonnull(2)]]
|
||||
void print_diagnostic(ModuleFile *file, TokenLocation *location, Message kind, const char *message);
|
||||
[[gnu::nonnull(1)]]
|
||||
void print_diagnostic(TokenLocation *location, Message kind, const char *message, ...);
|
||||
|
||||
[[gnu::nonnull(2)]]
|
||||
/**
|
||||
|
@ -142,15 +142,4 @@ const char* get_last_error();
|
|||
[[nodiscard("pointer must be freed")]]
|
||||
const char* get_absolute_path(const char* path);
|
||||
|
||||
/**
|
||||
* @brief Create a file path from a base name, extension a variable amount of directory path segments.
|
||||
* @param count Amount of path segments to prepend to the basename
|
||||
* @param name Basename of the file
|
||||
* @param ext Extension of the file
|
||||
* @param ... Path segments without path separator
|
||||
* @return A relative path of a file
|
||||
*/
|
||||
[[nodiscard("pointer must be freed")]]
|
||||
const char* make_file_path(const char* name, const char* ext, int count, ...);
|
||||
|
||||
#endif //GEMSTONE_FILES_H
|
||||
|
|
|
@ -98,10 +98,13 @@
|
|||
yytext = yytext +1;
|
||||
yytext[yyleng - 2] = 0;
|
||||
|
||||
DEBUG("\"%s\" tokenized with \'ValStr\'", yytext); yylval.string = mem_strdup(MemoryNamespaceLex, yytext); return(ValStr);};
|
||||
DEBUG("\"%s\" tokenized with \'ValStr\'", yytext);
|
||||
yylval.string = collapse_escape_sequences(yytext);
|
||||
return(ValStr);
|
||||
};
|
||||
\"\"\"[^\"]*\"\"\" {
|
||||
yytext = yytext +3;
|
||||
yytext[yyleng - 4] = 0;
|
||||
yytext[yyleng - 6] = 0;
|
||||
|
||||
DEBUG("\"%s\" tokenized with \'ValMultistr\'", yytext); yylval.string = mem_strdup(MemoryNamespaceLex, yytext); return(ValMultistr);};
|
||||
[ \r\t] { /* ignore whitespace */ };
|
||||
|
|
|
@ -84,3 +84,41 @@ int getNextLine(void) {
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct ConstEscSeq {
|
||||
char* esc;
|
||||
char* rep;
|
||||
};
|
||||
|
||||
static struct ConstEscSeq sequences[] = {
|
||||
{
|
||||
"\\n",
|
||||
"\n"
|
||||
},
|
||||
{
|
||||
"\\\\",
|
||||
"\\"
|
||||
},
|
||||
{
|
||||
"\\t",
|
||||
"\t"
|
||||
},
|
||||
{
|
||||
"\\r",
|
||||
"\r"
|
||||
}
|
||||
};
|
||||
|
||||
char* collapse_escape_sequences(char* string) {
|
||||
GString* unesc = g_string_new(string);
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
g_string_replace(unesc, sequences[i].esc, sequences[i].rep, 0);
|
||||
}
|
||||
|
||||
char* str = mem_strdup(MemoryNamespaceLex, unesc->str);
|
||||
|
||||
g_string_free(unesc, TRUE);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
|
|
@ -36,4 +36,6 @@ int nextChar(char *dst);
|
|||
*/
|
||||
int getNextLine(void);
|
||||
|
||||
char* collapse_escape_sequences(char* string);
|
||||
|
||||
#endif // LEX_UTIL_H_
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
|
||||
#include <assert.h>
|
||||
#include <ast/ast.h>
|
||||
#include <codegen/backend.h>
|
||||
#include <llvm-c/Core.h>
|
||||
#include <llvm-c/TargetMachine.h>
|
||||
#include <llvm/backend.h>
|
||||
#include <llvm/parser.h>
|
||||
#include <sys/log.h>
|
||||
|
||||
Target create_native_target() {
|
||||
DEBUG("creating native target...");
|
||||
Target target;
|
||||
|
||||
target.name.str = "tmp";
|
||||
target.name.allocation = NONE;
|
||||
|
||||
target.triple.str = LLVMGetDefaultTargetTriple();
|
||||
target.triple.allocation = LLVM;
|
||||
assert(target.triple.str != NULL);
|
||||
|
||||
target.cpu.str = LLVMGetHostCPUName();
|
||||
target.cpu.allocation = LLVM;
|
||||
assert(target.cpu.str != NULL);
|
||||
|
||||
target.features.str = LLVMGetHostCPUFeatures();
|
||||
target.features.allocation = LLVM;
|
||||
assert(target.features.str != NULL);
|
||||
|
||||
target.opt = LLVMCodeGenLevelNone;
|
||||
target.reloc = LLVMRelocDefault;
|
||||
target.model = LLVMCodeModelDefault;
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
LLVMCodeGenOptLevel llvm_opt_from_int(int level) {
|
||||
switch (level) {
|
||||
case 1:
|
||||
return LLVMCodeGenLevelLess;
|
||||
case 2:
|
||||
return LLVMCodeGenLevelDefault;
|
||||
case 3:
|
||||
return LLVMCodeGenLevelAggressive;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
PANIC("invalid code generation optimization level: %d", level);
|
||||
}
|
||||
|
||||
static char* create_target_output_name(const TargetConfig* config) {
|
||||
char* prefix = "";
|
||||
if (config->mode == Library) {
|
||||
prefix = "lib";
|
||||
}
|
||||
|
||||
return g_strjoin("", prefix, config->name, NULL);
|
||||
}
|
||||
|
||||
Target create_target_from_config(const TargetConfig* config) {
|
||||
DEBUG("Building target from configuration");
|
||||
|
||||
Target target = create_native_target();
|
||||
|
||||
target.name.str = create_target_output_name(config);
|
||||
target.name.allocation = NONE; // freed later by compiler
|
||||
|
||||
target.opt = llvm_opt_from_int(config->optimization_level);
|
||||
|
||||
INFO("Configured target: %s/%d: (%s) on %s { %s }", target.name.str, target.opt,
|
||||
target.triple.str, target.cpu.str, target.features.str);
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
static void delete_string(String string) {
|
||||
DEBUG("deleting string...");
|
||||
switch (string.allocation) {
|
||||
case LLVM:
|
||||
LLVMDisposeMessage(string.str);
|
||||
break;
|
||||
case LIBC:
|
||||
free(string.str);
|
||||
break;
|
||||
case NONE:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void delete_target(Target target) {
|
||||
delete_string(target.name);
|
||||
delete_string(target.cpu);
|
||||
delete_string(target.features);
|
||||
delete_string(target.triple);
|
||||
}
|
||||
|
||||
typedef enum LLVMBackendError_t { UnresolvedImport } LLVMBackendError;
|
||||
|
||||
static BackendError llvm_backend_codegen(const Module* unit,
|
||||
const TargetConfig* target) {
|
||||
return parse_module(unit, target);
|
||||
}
|
||||
|
||||
static BackendError llvm_backend_codegen_init(void) {
|
||||
return new_backend_error(Success);
|
||||
}
|
||||
|
||||
static BackendError llvm_backend_codegen_deinit(void) {
|
||||
return new_backend_error(Success);
|
||||
}
|
||||
|
||||
void llvm_backend_init() {
|
||||
BackendError err =
|
||||
set_backend(&llvm_backend_codegen_init, &llvm_backend_codegen_deinit,
|
||||
&llvm_backend_codegen, "LLVM");
|
||||
|
||||
if (err.kind != Success) {
|
||||
PANIC("unable to init llvm backend: %ld", err);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
|
||||
#ifndef LLVM_CODEGEN_BACKEND_H_
|
||||
#define LLVM_CODEGEN_BACKEND_H_
|
||||
|
||||
#include <llvm-c/TargetMachine.h>
|
||||
|
||||
enum StringAllocation_t {
|
||||
LLVM,
|
||||
LIBC,
|
||||
NONE
|
||||
};
|
||||
|
||||
typedef struct String_t {
|
||||
enum StringAllocation_t allocation;
|
||||
char* str;
|
||||
} String;
|
||||
|
||||
typedef struct Target_t {
|
||||
String name;
|
||||
String triple;
|
||||
String cpu;
|
||||
String features;
|
||||
LLVMCodeGenOptLevel opt;
|
||||
LLVMRelocMode reloc;
|
||||
LLVMCodeModel model;
|
||||
} Target;
|
||||
|
||||
Target create_native_target();
|
||||
|
||||
Target create_target_from_config(const TargetConfig* config);
|
||||
|
||||
void delete_target(Target target);
|
||||
|
||||
void llvm_backend_init(void);
|
||||
|
||||
#endif // LLVM_CODEGEN_BACKEND_H_
|
|
@ -0,0 +1,149 @@
|
|||
//
|
||||
// Created by servostar on 6/4/24.
|
||||
//
|
||||
|
||||
#include <llvm/link/lld.h>
|
||||
#include <sys/log.h>
|
||||
#include <mem/cache.h>
|
||||
#include <sys/col.h>
|
||||
|
||||
const char* get_absolute_link_path(const TargetConfig* config, const char* link_target_name) {
|
||||
INFO("resolving absolute path for link target: %s", link_target_name);
|
||||
|
||||
for (guint i = 0; i < config->link_search_paths->len; i++) {
|
||||
const char* link_directory_path = g_array_index(config->link_search_paths, char*, i);
|
||||
|
||||
char* path = g_build_filename(link_directory_path, link_target_name, NULL);
|
||||
char* cwd = g_get_current_dir();
|
||||
char* canonical = g_canonicalize_filename(path, cwd);
|
||||
|
||||
const gboolean exists = g_file_test(canonical, G_FILE_TEST_EXISTS);
|
||||
const gboolean is_dir = g_file_test(canonical, G_FILE_TEST_IS_DIR);
|
||||
|
||||
g_free(path);
|
||||
g_free(cwd);
|
||||
|
||||
if (exists && !is_dir) {
|
||||
INFO("link target found at: %s", canonical);
|
||||
return canonical;
|
||||
}
|
||||
|
||||
g_free(canonical);
|
||||
}
|
||||
|
||||
// file not found
|
||||
return NULL;
|
||||
}
|
||||
|
||||
TargetLinkConfig* lld_create_link_config(__attribute__((unused)) const Target* target, const TargetConfig* target_config, const Module* module) {
|
||||
DEBUG("generating link configuration");
|
||||
|
||||
TargetLinkConfig* config = mem_alloc(MemoryNamespaceLld, sizeof(TargetLinkConfig));
|
||||
|
||||
config->fatal_warnings = target_config->lld_fatal_warnings;
|
||||
config->object_file_names = g_array_new(FALSE, FALSE, sizeof(char*));
|
||||
config->colorize = stdout_supports_ansi_esc();
|
||||
|
||||
// append build object file
|
||||
char* basename = g_strjoin(".", target_config->name, "o", NULL);
|
||||
char* filename = g_build_filename(target_config->archive_directory, basename, NULL);
|
||||
const char* target_object = get_absolute_link_path(target_config, (const char*) filename);
|
||||
if (target_object == NULL) {
|
||||
ERROR("failed to resolve path to target object: %s", filename);
|
||||
lld_delete_link_config(config);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
{
|
||||
// output file after linking
|
||||
basename = g_strjoin(".", target_config->name, "out", NULL);
|
||||
filename = g_build_filename(target_config->output_directory, basename, NULL);
|
||||
|
||||
config->output_file = filename;
|
||||
}
|
||||
|
||||
g_array_append_val(config->object_file_names, target_object);
|
||||
INFO("resolved path of target object: %s", target_object);
|
||||
|
||||
// if it is an app, add entrypoint library
|
||||
if (target_config->mode == Application) {
|
||||
char* entrypoint = g_strdup("libentrypoint.a");
|
||||
g_array_append_val(module->imports, entrypoint);
|
||||
}
|
||||
|
||||
// resolve absolute paths to dependent library object files
|
||||
DEBUG("resolving target dependencies...");
|
||||
for (guint i = 0; i < module->imports->len; i++) {
|
||||
const char* dependency = g_array_index(module->imports, const char*, i);
|
||||
|
||||
const char* dependency_object = get_absolute_link_path(target_config, dependency);
|
||||
if (dependency_object == NULL) {
|
||||
ERROR("failed to resolve path to dependency object: %s", dependency);
|
||||
lld_delete_link_config(config);
|
||||
return NULL;
|
||||
}
|
||||
g_array_append_val(config->object_file_names, dependency_object);
|
||||
INFO("resolved path of target object: %s", dependency_object);
|
||||
}
|
||||
|
||||
INFO("resolved %d dependencies", config->object_file_names->len);
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
GArray* lld_create_lld_arguments(TargetLinkConfig* config) {
|
||||
GArray* argv = g_array_new(TRUE, FALSE, sizeof(char*));
|
||||
|
||||
gchar* arg = g_strdup("ld.lld");
|
||||
g_array_append_val(argv, arg);
|
||||
|
||||
if (config->fatal_warnings) {
|
||||
arg = g_strdup("--fatal-warnings");
|
||||
g_array_append_val(argv, arg);
|
||||
}
|
||||
|
||||
if (config->colorize) {
|
||||
arg = g_strdup("--color-diagnostics=always");
|
||||
g_array_append_val(argv, arg);
|
||||
}
|
||||
|
||||
{
|
||||
arg = g_strjoin("", "-o", config->output_file, NULL);
|
||||
g_array_append_val(argv, arg);
|
||||
}
|
||||
|
||||
for (guint i = 0; i < config->object_file_names->len; i++) {
|
||||
char* object_file_path = g_array_index(config->object_file_names, char*, i);
|
||||
arg = g_strjoin("", object_file_path, NULL);
|
||||
g_array_append_val(argv, arg);
|
||||
}
|
||||
|
||||
return argv;
|
||||
}
|
||||
|
||||
BackendError lld_link_target(TargetLinkConfig* config) {
|
||||
DEBUG("linking target...");
|
||||
BackendError err = SUCCESS;
|
||||
|
||||
GArray* argv = lld_create_lld_arguments(config);
|
||||
|
||||
INFO("Linking target...");
|
||||
|
||||
char* arguments = g_strjoinv(" ", (char**) argv->data);
|
||||
print_message(Info, "%s", arguments);
|
||||
g_free(arguments);
|
||||
|
||||
INFO("done linking target...");
|
||||
|
||||
g_array_free(argv, TRUE);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void lld_delete_link_config(TargetLinkConfig* config) {
|
||||
for (guint i = 0; i < config->object_file_names->len; i++) {
|
||||
free((void*) g_array_index(config->object_file_names, const char*, i));
|
||||
}
|
||||
g_array_free(config->object_file_names, TRUE);
|
||||
mem_free(config);
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
//
|
||||
// Created by servostar on 6/4/24.
|
||||
//
|
||||
|
||||
#ifndef LLVM_BACKEND_LLD_H
|
||||
#define LLVM_BACKEND_LLD_H
|
||||
|
||||
#include <codegen/backend.h>
|
||||
#include <llvm/backend.h>
|
||||
|
||||
TargetLinkConfig* lld_create_link_config(__attribute__((unused)) const Target * target, const TargetConfig* target_config, const Module* module);
|
||||
|
||||
BackendError lld_link_target(TargetLinkConfig* config);
|
||||
|
||||
void lld_delete_link_config(TargetLinkConfig* config);
|
||||
|
||||
#endif // LLVM_BACKEND_LLD_H
|
|
@ -0,0 +1,471 @@
|
|||
//
|
||||
// Created by servostar on 5/28/24.
|
||||
//
|
||||
|
||||
#include <llvm/llvm-ir/expr.h>
|
||||
#include <llvm/llvm-ir/types.h>
|
||||
#include <sys/log.h>
|
||||
#include <mem/cache.h>
|
||||
|
||||
BackendError impl_bitwise_operation(LLVMBackendCompileUnit *unit,
|
||||
LLVMLocalScope *scope,
|
||||
LLVMBuilderRef builder,
|
||||
Operation *operation,
|
||||
LLVMValueRef *llvm_result) {
|
||||
Expression *rhs = NULL;
|
||||
Expression *lhs = NULL;
|
||||
LLVMValueRef llvm_rhs = NULL;
|
||||
LLVMValueRef llvm_lhs = NULL;
|
||||
|
||||
if (operation->impl.bitwise == BitwiseNot) {
|
||||
// single operand
|
||||
rhs = g_array_index(operation->operands, Expression*, 0);
|
||||
impl_expr(unit, scope, builder, rhs, FALSE, &llvm_rhs);
|
||||
} else {
|
||||
// two operands
|
||||
lhs = g_array_index(operation->operands, Expression*, 0);
|
||||
impl_expr(unit, scope, builder, lhs, FALSE, &llvm_lhs);
|
||||
|
||||
rhs = g_array_index(operation->operands, Expression*, 1);
|
||||
impl_expr(unit, scope, builder, rhs, FALSE, &llvm_rhs);
|
||||
}
|
||||
|
||||
switch (operation->impl.bitwise) {
|
||||
case BitwiseAnd:
|
||||
*llvm_result = LLVMBuildAnd(builder, llvm_lhs, llvm_rhs, "bitwise and");
|
||||
break;
|
||||
case BitwiseOr:
|
||||
*llvm_result = LLVMBuildOr(builder, llvm_lhs, llvm_rhs, "bitwise or");
|
||||
break;
|
||||
case BitwiseXor:
|
||||
*llvm_result = LLVMBuildXor(builder, llvm_lhs, llvm_rhs, "bitwise xor");
|
||||
break;
|
||||
case BitwiseNot:
|
||||
*llvm_result = LLVMBuildNot(builder, llvm_rhs, "bitwise not");
|
||||
break;
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert any integral type (integer) to a boolean value.
|
||||
* A boolean value hereby meaning an integer of the same type as the input
|
||||
* value but with the value of either 0 or one.
|
||||
* @param builder
|
||||
* @param integral
|
||||
* @return
|
||||
*/
|
||||
[[maybe_unused]]
|
||||
static LLVMValueRef convert_integral_to_boolean(
|
||||
LLVMBuilderRef builder, LLVMValueRef integral) {
|
||||
// type of input
|
||||
LLVMTypeRef valueType = LLVMTypeOf(integral);
|
||||
// zero value of same type as integral
|
||||
LLVMValueRef zero = LLVMConstIntOfString(valueType, "0", 10);
|
||||
// returns 1 if integral is not zero and zero otherwise
|
||||
return LLVMBuildICmp(builder, LLVMIntNE, zero, integral, "to boolean");
|
||||
}
|
||||
|
||||
BackendError impl_logical_operation(LLVMBackendCompileUnit *unit,
|
||||
LLVMLocalScope *scope,
|
||||
LLVMBuilderRef builder,
|
||||
Operation *operation,
|
||||
LLVMValueRef *llvm_result) {
|
||||
Expression *rhs = NULL;
|
||||
Expression *lhs = NULL;
|
||||
LLVMValueRef llvm_rhs = NULL;
|
||||
LLVMValueRef llvm_lhs = NULL;
|
||||
|
||||
if (operation->impl.logical == LogicalNot) {
|
||||
// single operand
|
||||
rhs = g_array_index(operation->operands, Expression*, 0);
|
||||
impl_expr(unit, scope, builder, rhs, FALSE, &llvm_rhs);
|
||||
} else {
|
||||
// two operands
|
||||
lhs = g_array_index(operation->operands, Expression*, 0);
|
||||
impl_expr(unit, scope, builder, lhs, FALSE, &llvm_lhs);
|
||||
|
||||
rhs = g_array_index(operation->operands, Expression*, 1);
|
||||
impl_expr(unit, scope, builder, rhs, FALSE, &llvm_rhs);
|
||||
}
|
||||
|
||||
switch (operation->impl.logical) {
|
||||
case LogicalAnd:
|
||||
*llvm_result = LLVMBuildAnd(builder, llvm_lhs, llvm_rhs, "logical and");
|
||||
break;
|
||||
case LogicalOr:
|
||||
*llvm_result = LLVMBuildOr(builder, llvm_lhs, llvm_rhs, "logical or");
|
||||
break;
|
||||
case LogicalXor:
|
||||
*llvm_result = LLVMBuildXor(builder, llvm_lhs, llvm_rhs, "logical xor");
|
||||
break;
|
||||
case LogicalNot:
|
||||
*llvm_result = LLVMBuildNot(builder, llvm_rhs, "logical not");
|
||||
break;
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
static LLVMBool is_floating_point(Type *value) {
|
||||
if (value->kind == TypeKindPrimitive) {
|
||||
return value->impl.primitive == Float;
|
||||
}
|
||||
|
||||
if (value->kind == TypeKindComposite) {
|
||||
return value->impl.composite.primitive == Float;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static LLVMBool is_integral(Type *value) {
|
||||
if (value->kind == TypeKindPrimitive) {
|
||||
return value->impl.primitive == Int;
|
||||
}
|
||||
|
||||
if (value->kind == TypeKindComposite) {
|
||||
return value->impl.composite.primitive == Int;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BackendError impl_relational_operation(LLVMBackendCompileUnit *unit,
|
||||
LLVMLocalScope *scope,
|
||||
LLVMBuilderRef builder,
|
||||
Operation *operation,
|
||||
LLVMValueRef *llvm_result) {
|
||||
Expression *rhs = NULL;
|
||||
Expression *lhs = NULL;
|
||||
LLVMValueRef llvm_rhs = NULL;
|
||||
LLVMValueRef llvm_lhs = NULL;
|
||||
|
||||
// two operands
|
||||
lhs = g_array_index(operation->operands, Expression*, 0);
|
||||
impl_expr(unit, scope, builder, lhs, FALSE, &llvm_lhs);
|
||||
|
||||
rhs = g_array_index(operation->operands, Expression*, 1);
|
||||
impl_expr(unit, scope, builder, rhs, FALSE, &llvm_rhs);
|
||||
|
||||
if (is_integral(rhs->result)) {
|
||||
// integral type
|
||||
LLVMIntPredicate operator = 0;
|
||||
|
||||
switch (operation->impl.relational) {
|
||||
case Equal:
|
||||
operator = LLVMIntEQ;
|
||||
break;
|
||||
case Greater:
|
||||
operator = LLVMIntSGT;
|
||||
break;
|
||||
case Less:
|
||||
operator = LLVMIntSLT;
|
||||
break;
|
||||
}
|
||||
|
||||
*llvm_result = LLVMBuildICmp(builder, operator, llvm_lhs, llvm_rhs, "integral comparison");
|
||||
|
||||
} else if (is_floating_point(rhs->result)) {
|
||||
// integral type
|
||||
LLVMRealPredicate operator = 0;
|
||||
|
||||
switch (operation->impl.relational) {
|
||||
case Equal:
|
||||
operator = LLVMRealOEQ;
|
||||
break;
|
||||
case Greater:
|
||||
operator = LLVMRealOGT;
|
||||
break;
|
||||
case Less:
|
||||
operator = LLVMRealOLT;
|
||||
break;
|
||||
}
|
||||
|
||||
*llvm_result = LLVMBuildFCmp(builder, operator, llvm_lhs, llvm_rhs, "floating point comparison");
|
||||
} else {
|
||||
PANIC("invalid type for relational operator");
|
||||
}
|
||||
|
||||
// *llvm_result = convert_integral_to_boolean(builder, *llvm_result);
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
BackendError impl_arithmetic_operation(LLVMBackendCompileUnit *unit,
|
||||
LLVMLocalScope *scope,
|
||||
LLVMBuilderRef builder,
|
||||
Operation *operation,
|
||||
LLVMValueRef *llvm_result) {
|
||||
Expression *rhs = NULL;
|
||||
Expression *lhs = NULL;
|
||||
LLVMValueRef llvm_rhs = NULL;
|
||||
LLVMValueRef llvm_lhs = NULL;
|
||||
|
||||
if (operation->impl.arithmetic == Negate) {
|
||||
// single operand
|
||||
rhs = g_array_index(operation->operands, Expression*, 0);
|
||||
impl_expr(unit, scope, builder, rhs, FALSE, &llvm_rhs);
|
||||
} else {
|
||||
// two operands
|
||||
lhs = g_array_index(operation->operands, Expression*, 0);
|
||||
impl_expr(unit, scope, builder, lhs, FALSE, &llvm_lhs);
|
||||
|
||||
rhs = g_array_index(operation->operands, Expression*, 1);
|
||||
impl_expr(unit, scope, builder, rhs, FALSE, &llvm_rhs);
|
||||
}
|
||||
|
||||
if (is_integral(rhs->result)) {
|
||||
|
||||
switch (operation->impl.arithmetic) {
|
||||
case Add:
|
||||
*llvm_result = LLVMBuildNSWAdd(builder, llvm_lhs, llvm_rhs, "signed integer addition");
|
||||
break;
|
||||
case Sub:
|
||||
*llvm_result = LLVMBuildNSWSub(builder, llvm_lhs, llvm_rhs, "signed integer subtraction");
|
||||
break;
|
||||
case Mul:
|
||||
*llvm_result = LLVMBuildNSWMul(builder, llvm_lhs, llvm_rhs, "signed integer multiply");
|
||||
break;
|
||||
case Div:
|
||||
*llvm_result = LLVMBuildSDiv(builder, llvm_lhs, llvm_rhs, "signed integer divide");
|
||||
break;
|
||||
case Negate:
|
||||
*llvm_result = LLVMBuildNeg(builder, llvm_rhs, "signed integer negate");
|
||||
break;
|
||||
}
|
||||
|
||||
} else if (is_floating_point(rhs->result)) {
|
||||
|
||||
switch (operation->impl.arithmetic) {
|
||||
case Add:
|
||||
*llvm_result = LLVMBuildFAdd(builder, llvm_lhs, llvm_rhs, "floating point addition");
|
||||
break;
|
||||
case Sub:
|
||||
*llvm_result = LLVMBuildFSub(builder, llvm_lhs, llvm_rhs, "floating point subtraction");
|
||||
break;
|
||||
case Mul:
|
||||
*llvm_result = LLVMBuildFMul(builder, llvm_lhs, llvm_rhs, "floating point multiply");
|
||||
break;
|
||||
case Div:
|
||||
*llvm_result = LLVMBuildFDiv(builder, llvm_lhs, llvm_rhs, "floating point divide");
|
||||
break;
|
||||
case Negate:
|
||||
*llvm_result = LLVMBuildFNeg(builder, llvm_rhs, "floating point negate");
|
||||
break;
|
||||
}
|
||||
|
||||
} else {
|
||||
PANIC("invalid type for arithmetic operator");
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
BackendError impl_operation(LLVMBackendCompileUnit *unit, LLVMLocalScope *scope,
|
||||
LLVMBuilderRef builder, Operation *operation,
|
||||
LLVMValueRef *llvm_result) {
|
||||
BackendError err;
|
||||
|
||||
switch (operation->kind) {
|
||||
case Bitwise:
|
||||
err = impl_bitwise_operation(unit, scope, builder, operation,
|
||||
llvm_result);
|
||||
break;
|
||||
case Boolean:
|
||||
err = impl_logical_operation(unit, scope, builder, operation,
|
||||
llvm_result);
|
||||
break;
|
||||
case Relational:
|
||||
err = impl_relational_operation(unit, scope, builder, operation,
|
||||
llvm_result);
|
||||
break;
|
||||
case Arithmetic:
|
||||
err = impl_arithmetic_operation(unit, scope, builder, operation,
|
||||
llvm_result);
|
||||
break;
|
||||
default:
|
||||
PANIC("Invalid operator");
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
BackendError impl_transmute(LLVMBackendCompileUnit *unit, LLVMLocalScope *scope,
|
||||
LLVMBuilderRef builder, Transmute *transmute,
|
||||
LLVMValueRef *llvm_result) {
|
||||
|
||||
LLVMValueRef operand = NULL;
|
||||
impl_expr(unit, scope, builder, transmute->operand, FALSE, &operand);
|
||||
|
||||
LLVMTypeRef target_type = NULL;
|
||||
BackendError err = get_type_impl(unit, scope->func_scope->global_scope,
|
||||
transmute->targetType, &target_type);
|
||||
// if target type is valid
|
||||
if (err.kind == Success) {
|
||||
*llvm_result =
|
||||
LLVMBuildBitCast(builder, operand, target_type, "transmute");
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static LLVMBool is_type_signed(const Type *type) {
|
||||
switch (type->kind) {
|
||||
case TypeKindPrimitive:
|
||||
return 1;
|
||||
case TypeKindComposite:
|
||||
return type->impl.composite.sign == Signed;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
BackendError impl_typecast(LLVMBackendCompileUnit *unit, LLVMLocalScope *scope,
|
||||
LLVMBuilderRef builder, TypeCast *typecast,
|
||||
LLVMValueRef *llvm_result) {
|
||||
LLVMValueRef operand = NULL;
|
||||
impl_expr(unit, scope, builder, typecast->operand, FALSE, &operand);
|
||||
|
||||
LLVMTypeRef target_type = NULL;
|
||||
BackendError err = get_type_impl(unit, scope->func_scope->global_scope,
|
||||
typecast->targetType, &target_type);
|
||||
// if target type is valid
|
||||
if (err.kind != Success) {
|
||||
return err;
|
||||
}
|
||||
|
||||
LLVMBool dst_signed = is_type_signed(typecast->targetType);
|
||||
LLVMBool src_signed = is_type_signed(typecast->operand->result);
|
||||
const LLVMOpcode opcode =
|
||||
LLVMGetCastOpcode(operand, src_signed, target_type, dst_signed);
|
||||
*llvm_result =
|
||||
LLVMBuildCast(builder, opcode, operand, target_type, "expr.typecast");
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
BackendError impl_variable_load(LLVMBackendCompileUnit *unit, LLVMLocalScope *scope,
|
||||
LLVMBuilderRef builder, Variable *variable,
|
||||
LLVMBool reference,
|
||||
LLVMValueRef *llvm_result) {
|
||||
|
||||
LLVMValueRef llvm_variable = get_variable(scope, variable->name);
|
||||
|
||||
Type* type;
|
||||
|
||||
if (variable->kind == VariableKindDefinition) {
|
||||
type = variable->impl.definiton.declaration.type;
|
||||
} else {
|
||||
type = variable->impl.declaration.type;
|
||||
}
|
||||
|
||||
if (llvm_variable == NULL) {
|
||||
return new_backend_impl_error(Implementation, NULL, "Variable not found");
|
||||
}
|
||||
|
||||
if (reference) {
|
||||
// don't load in case variable is parameter?
|
||||
// only reference wanted
|
||||
|
||||
*llvm_result = llvm_variable;
|
||||
|
||||
} else {
|
||||
// no referencing, load value
|
||||
LLVMTypeRef llvm_type;
|
||||
|
||||
get_type_impl(unit, scope->func_scope->global_scope, type, &llvm_type);
|
||||
|
||||
if (LLVMGetTypeKind(LLVMTypeOf(llvm_variable)) == LLVMPointerTypeKind) {
|
||||
*llvm_result = LLVMBuildLoad2(builder, llvm_type, llvm_variable, "");
|
||||
} else {
|
||||
*llvm_result = llvm_variable;
|
||||
}
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
BackendError impl_address_of(LLVMBackendCompileUnit *unit, LLVMLocalScope *scope,
|
||||
LLVMBuilderRef builder, AddressOf* addressOf,
|
||||
LLVMValueRef *llvm_result) {
|
||||
|
||||
BackendError err = impl_expr(unit, scope, builder, addressOf->variable, FALSE, llvm_result);
|
||||
|
||||
if (err.kind != Success) {
|
||||
return err;
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
BackendError impl_deref(LLVMBackendCompileUnit *unit, LLVMLocalScope *scope,
|
||||
LLVMBuilderRef builder, Dereference* dereference,
|
||||
LLVMValueRef *llvm_result) {
|
||||
BackendError err;
|
||||
|
||||
LLVMValueRef llvm_pointer = get_variable(scope, dereference->variable->impl.variable->name);
|
||||
LLVMTypeRef llvm_deref_type = NULL;
|
||||
err = get_type_impl(unit, scope->func_scope->global_scope, dereference->variable->result->impl.reference, &llvm_deref_type);
|
||||
if (err.kind != Success) {
|
||||
return err;
|
||||
}
|
||||
|
||||
LLVMValueRef* index = mem_alloc(MemoryNamespaceLlvm, sizeof(LLVMValueRef));
|
||||
err = impl_expr(unit, scope, builder, dereference->index, FALSE, index);
|
||||
if (err.kind != Success) {
|
||||
return err;
|
||||
}
|
||||
|
||||
*llvm_result = LLVMBuildGEP2(builder, llvm_deref_type, llvm_pointer, index, 1, "expr.deref.gep2");
|
||||
|
||||
*llvm_result = LLVMBuildLoad2(builder, llvm_deref_type, *llvm_result, "expr.deref.load");
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
BackendError impl_expr(LLVMBackendCompileUnit *unit, LLVMLocalScope *scope,
|
||||
LLVMBuilderRef builder, Expression *expr,
|
||||
LLVMBool reference,
|
||||
LLVMValueRef *llvm_result) {
|
||||
DEBUG("implementing expression: %ld", expr->kind);
|
||||
BackendError err = SUCCESS;
|
||||
|
||||
switch (expr->kind) {
|
||||
case ExpressionKindConstant:
|
||||
err = get_const_type_value(unit, scope->func_scope->global_scope,
|
||||
&expr->impl.constant, llvm_result);
|
||||
break;
|
||||
case ExpressionKindTransmute:
|
||||
err = impl_transmute(unit, scope, builder, &expr->impl.transmute,
|
||||
llvm_result);
|
||||
break;
|
||||
case ExpressionKindTypeCast:
|
||||
err = impl_typecast(unit, scope, builder, &expr->impl.typecast,
|
||||
llvm_result);
|
||||
break;
|
||||
case ExpressionKindOperation:
|
||||
err = impl_operation(unit, scope, builder, &expr->impl.operation,
|
||||
llvm_result);
|
||||
break;
|
||||
case ExpressionKindVariable:
|
||||
err = impl_variable_load(unit, scope, builder, expr->impl.variable,
|
||||
reference,
|
||||
llvm_result);
|
||||
break;
|
||||
case ExpressionKindAddressOf:
|
||||
err = impl_address_of(unit, scope, builder, &expr->impl.addressOf,
|
||||
llvm_result);
|
||||
break;
|
||||
case ExpressionKindDereference:
|
||||
err = impl_deref(unit, scope, builder, &expr->impl.dereference,
|
||||
llvm_result);
|
||||
break;
|
||||
default:
|
||||
err = new_backend_impl_error(Implementation, NULL, "unknown expression");
|
||||
break;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
//
|
||||
// Created by servostar on 5/28/24.
|
||||
//
|
||||
|
||||
#ifndef LLVM_BACKEND_EXPR_H
|
||||
#define LLVM_BACKEND_EXPR_H
|
||||
|
||||
#include <codegen/backend.h>
|
||||
#include <llvm-c/Types.h>
|
||||
#include <llvm/llvm-ir/func.h>
|
||||
#include <llvm/parser.h>
|
||||
|
||||
BackendError impl_expr(LLVMBackendCompileUnit *unit, LLVMLocalScope *scope,
|
||||
LLVMBuilderRef builder, Expression *expr,
|
||||
LLVMBool reference,
|
||||
LLVMValueRef *llvm_result);
|
||||
|
||||
#endif // LLVM_BACKEND_EXPR_H
|
|
@ -0,0 +1,256 @@
|
|||
|
||||
#include <codegen/backend.h>
|
||||
#include <llvm-c/Core.h>
|
||||
#include <llvm-c/Types.h>
|
||||
#include <llvm/llvm-ir/func.h>
|
||||
#include <llvm/parser.h>
|
||||
#include <llvm/llvm-ir/types.h>
|
||||
#include <llvm/llvm-ir/stmt.h>
|
||||
#include <llvm/llvm-ir/variables.h>
|
||||
#include <set/types.h>
|
||||
#include <sys/log.h>
|
||||
#include <mem/cache.h>
|
||||
|
||||
LLVMLocalScope* new_local_scope(LLVMLocalScope* parent) {
|
||||
LLVMLocalScope* scope = malloc(sizeof(LLVMLocalScope));
|
||||
|
||||
scope->func_scope = parent->func_scope;
|
||||
scope->vars = g_hash_table_new(g_str_hash, g_str_equal);
|
||||
scope->parent_scope = parent;
|
||||
|
||||
return scope;
|
||||
}
|
||||
|
||||
void delete_local_scope(LLVMLocalScope* scope) {
|
||||
g_hash_table_destroy(scope->vars);
|
||||
free(scope);
|
||||
}
|
||||
|
||||
static LLVMValueRef get_parameter(const LLVMFuncScope* scope,
|
||||
const char* name) {
|
||||
if (g_hash_table_contains(scope->params, name)) {
|
||||
return g_hash_table_lookup(scope->params, name);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
LLVMValueRef get_variable(const LLVMLocalScope* scope, const char* name) {
|
||||
if (g_hash_table_contains(scope->vars, name)) {
|
||||
return g_hash_table_lookup(scope->vars, name);
|
||||
}
|
||||
|
||||
if (scope->parent_scope != NULL) {
|
||||
return get_variable(scope->parent_scope, name);
|
||||
}
|
||||
|
||||
LLVMValueRef param = get_parameter(scope->func_scope, name);
|
||||
if (param != NULL) {
|
||||
return param;
|
||||
}
|
||||
|
||||
LLVMValueRef global_var = get_global_variable(scope->func_scope->global_scope, (char*) name);
|
||||
return global_var;
|
||||
}
|
||||
|
||||
LLVMBool is_parameter(const LLVMLocalScope* scope, const char* name) {
|
||||
if (g_hash_table_contains(scope->vars, name)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (scope->parent_scope != NULL) {
|
||||
return is_parameter(scope->parent_scope, name);
|
||||
}
|
||||
|
||||
LLVMValueRef param = get_parameter(scope->func_scope, name);
|
||||
if (param != NULL) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
LLVMValueRef global_var = get_global_variable(scope->func_scope->global_scope, (char*) name);
|
||||
return global_var != NULL;
|
||||
}
|
||||
|
||||
BackendError impl_param_type(LLVMBackendCompileUnit* unit,
|
||||
LLVMGlobalScope* scope, Parameter* param,
|
||||
LLVMTypeRef* llvm_type) {
|
||||
BackendError err = SUCCESS;
|
||||
|
||||
Type* gemstone_type = NULL;
|
||||
IO_Qualifier qualifier;
|
||||
|
||||
if (param->kind == ParameterDeclarationKind) {
|
||||
gemstone_type = param->impl.declaration.type;
|
||||
qualifier = param->impl.declaration.qualifier;
|
||||
} else {
|
||||
gemstone_type = param->impl.definiton.declaration.type;
|
||||
qualifier = param->impl.definiton.declaration.qualifier;
|
||||
}
|
||||
|
||||
// wrap output variables as pointers
|
||||
if (qualifier == Out || qualifier == InOut) {
|
||||
Type* reference_type = alloca(sizeof(Type));
|
||||
|
||||
reference_type->kind = TypeKindReference;
|
||||
reference_type->impl.reference = gemstone_type;
|
||||
|
||||
gemstone_type = reference_type;
|
||||
}
|
||||
|
||||
err = get_type_impl(unit, scope, gemstone_type, llvm_type);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
BackendError impl_func_type(LLVMBackendCompileUnit* unit,
|
||||
LLVMGlobalScope* scope, Function* func,
|
||||
LLVMValueRef* llvm_fun) {
|
||||
DEBUG("implementing function declaration: %s()", func->name);
|
||||
BackendError err = SUCCESS;
|
||||
|
||||
GArray* llvm_params = g_array_new(FALSE, FALSE, sizeof(LLVMTypeRef));
|
||||
GArray* func_params = NULL;
|
||||
|
||||
if (func->kind == FunctionDeclarationKind) {
|
||||
func_params = func->impl.declaration.parameter;
|
||||
} else {
|
||||
func_params = func->impl.definition.parameter;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < func_params->len; i++) {
|
||||
Parameter* param = &g_array_index(func_params, Parameter, i);
|
||||
|
||||
LLVMTypeRef llvm_type = NULL;
|
||||
err = impl_param_type(unit, scope, param, &llvm_type);
|
||||
|
||||
if (err.kind != Success) {
|
||||
return err;
|
||||
}
|
||||
|
||||
g_array_append_val(llvm_params, llvm_type);
|
||||
}
|
||||
|
||||
DEBUG("implemented %ld parameter", llvm_params->len);
|
||||
|
||||
LLVMTypeRef llvm_fun_type =
|
||||
LLVMFunctionType(LLVMVoidTypeInContext(unit->context),
|
||||
(LLVMTypeRef*)llvm_params->data, llvm_params->len, 0);
|
||||
|
||||
*llvm_fun = LLVMAddFunction(unit->module, func->name, llvm_fun_type);
|
||||
|
||||
g_hash_table_insert(scope->functions, (char*) func->name, llvm_fun_type);
|
||||
|
||||
g_array_free(llvm_params, FALSE);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
BackendError impl_func_def(LLVMBackendCompileUnit* unit,
|
||||
LLVMGlobalScope* global_scope,
|
||||
Function* func, const char* name) {
|
||||
BackendError err = SUCCESS;
|
||||
|
||||
LLVMValueRef llvm_func = LLVMGetNamedFunction(unit->module, name);
|
||||
|
||||
if (err.kind == Success) {
|
||||
// create local function scope
|
||||
// NOTE: lives till the end of the function
|
||||
LLVMFuncScope* func_scope = alloca(sizeof(LLVMFuncScope));
|
||||
|
||||
func_scope->llvm_func = llvm_func;
|
||||
func_scope->global_scope = global_scope;
|
||||
func_scope->params = g_hash_table_new(g_str_hash, g_str_equal);
|
||||
|
||||
// create function body builder
|
||||
LLVMBasicBlockRef entry =
|
||||
LLVMAppendBasicBlockInContext(unit->context, llvm_func, "func.entry");
|
||||
LLVMBuilderRef builder = LLVMCreateBuilderInContext(unit->context);
|
||||
LLVMPositionBuilderAtEnd(builder, entry);
|
||||
|
||||
// create value references for parameter
|
||||
for (guint i = 0; i < func->impl.definition.parameter->len; i++) {
|
||||
Parameter* param = &g_array_index(func->impl.definition.parameter, Parameter, i);
|
||||
LLVMValueRef llvm_param = LLVMGetParam(llvm_func, i);
|
||||
|
||||
if (llvm_param == NULL) {
|
||||
return new_backend_impl_error(Implementation, NULL, "invalid parameter");
|
||||
}
|
||||
|
||||
g_hash_table_insert(func_scope->params, (gpointer)param->name, llvm_param);
|
||||
}
|
||||
|
||||
LLVMBasicBlockRef llvm_start_body_block = NULL;
|
||||
LLVMBasicBlockRef llvm_end_body_block = NULL;
|
||||
err = impl_block(unit, builder, func_scope, &llvm_start_body_block, &llvm_end_body_block, func->impl.definition.body);
|
||||
|
||||
if (err.kind == Success) {
|
||||
LLVMPositionBuilderAtEnd(builder, entry);
|
||||
LLVMBuildBr(builder, llvm_start_body_block);
|
||||
|
||||
// insert returning end block
|
||||
LLVMBasicBlockRef end_block =
|
||||
LLVMAppendBasicBlockInContext(unit->context, llvm_func, "func.end");
|
||||
LLVMPositionBuilderAtEnd(builder, end_block);
|
||||
LLVMBuildRetVoid(builder);
|
||||
|
||||
LLVMPositionBuilderAtEnd(builder, llvm_end_body_block);
|
||||
LLVMBuildBr(builder, end_block);
|
||||
|
||||
LLVMDisposeBuilder(builder);
|
||||
}
|
||||
|
||||
// delete function scope GLib structs
|
||||
g_hash_table_destroy(func_scope->params);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
BackendError impl_function_types(LLVMBackendCompileUnit* unit,
|
||||
LLVMGlobalScope* scope, GHashTable* functions) {
|
||||
DEBUG("implementing functions...");
|
||||
GHashTableIter iterator;
|
||||
g_hash_table_iter_init(&iterator, functions);
|
||||
|
||||
gpointer key = NULL;
|
||||
gpointer val = NULL;
|
||||
|
||||
BackendError err = SUCCESS;
|
||||
while (g_hash_table_iter_next(&iterator, &key, &val) != FALSE) {
|
||||
Function* func = (Function*) val;
|
||||
LLVMValueRef llvm_func;
|
||||
err = impl_func_type(unit, scope, func, &llvm_func);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
BackendError impl_functions(LLVMBackendCompileUnit* unit,
|
||||
LLVMGlobalScope* scope, GHashTable* functions) {
|
||||
DEBUG("implementing functions...");
|
||||
GHashTableIter iterator;
|
||||
g_hash_table_iter_init(&iterator, functions);
|
||||
|
||||
gpointer key = NULL;
|
||||
gpointer val = NULL;
|
||||
|
||||
BackendError err = SUCCESS;
|
||||
|
||||
size_t function_count = 0;
|
||||
while (g_hash_table_iter_next(&iterator, &key, &val) != FALSE) {
|
||||
Function* func = (Function*) val;
|
||||
|
||||
if (func->kind != FunctionDeclarationKind) {
|
||||
err = impl_func_def(unit, scope, func, (const char*)key);
|
||||
}
|
||||
|
||||
if (err.kind != Success) {
|
||||
return err;
|
||||
}
|
||||
|
||||
function_count++;
|
||||
}
|
||||
INFO("implemented %ld functions", function_count);
|
||||
|
||||
return err;
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
|
||||
#ifndef LLVM_BACKEND_FUNC_H_
|
||||
#define LLVM_BACKEND_FUNC_H_
|
||||
|
||||
#include <llvm/parser.h>
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
typedef struct LLVMFuncScope_t {
|
||||
LLVMGlobalScope* global_scope;
|
||||
// of LLVMTypeRef
|
||||
GHashTable* params;
|
||||
LLVMValueRef llvm_func;
|
||||
} LLVMFuncScope;
|
||||
|
||||
typedef struct LLVMLocalScope_t LLVMLocalScope;
|
||||
|
||||
typedef struct LLVMLocalScope_t {
|
||||
// of LLVMTypeRef
|
||||
GHashTable* vars;
|
||||
LLVMFuncScope* func_scope;
|
||||
LLVMLocalScope* parent_scope;
|
||||
} LLVMLocalScope;
|
||||
|
||||
LLVMLocalScope* new_local_scope(LLVMLocalScope* parent);
|
||||
|
||||
void delete_local_scope(LLVMLocalScope*);
|
||||
|
||||
LLVMValueRef get_variable(const LLVMLocalScope* scope, const char* name);
|
||||
|
||||
LLVMBool is_parameter(const LLVMLocalScope* scope, const char* name);
|
||||
|
||||
BackendError impl_function_types(LLVMBackendCompileUnit* unit,
|
||||
LLVMGlobalScope* scope,
|
||||
GHashTable* variables);
|
||||
|
||||
BackendError impl_functions(LLVMBackendCompileUnit* unit,
|
||||
LLVMGlobalScope* scope,
|
||||
GHashTable* variables);
|
||||
|
||||
#endif // LLVM_BACKEND_FUNC_H_
|
|
@ -0,0 +1,484 @@
|
|||
//
|
||||
// Created by servostar on 5/28/24.
|
||||
//
|
||||
|
||||
#include <codegen/backend.h>
|
||||
#include <sys/log.h>
|
||||
#include <llvm/parser.h>
|
||||
#include <llvm/llvm-ir/stmt.h>
|
||||
#include <llvm/llvm-ir/expr.h>
|
||||
#include <llvm/llvm-ir/func.h>
|
||||
#include <llvm/llvm-ir/types.h>
|
||||
#include <assert.h>
|
||||
#include <mem/cache.h>
|
||||
|
||||
BackendError impl_storage_expr(
|
||||
LLVMBackendCompileUnit *unit,
|
||||
LLVMBuilderRef
|
||||
builder,
|
||||
LLVMLocalScope *scope,
|
||||
const StorageExpr *expr,
|
||||
LLVMValueRef* storage_target) {
|
||||
|
||||
BackendError err = SUCCESS;
|
||||
|
||||
switch (expr->kind) {
|
||||
case StorageExprKindVariable:
|
||||
*storage_target =
|
||||
get_variable(scope, expr->impl.variable->name);
|
||||
break;
|
||||
case StorageExprKindDereference:
|
||||
|
||||
LLVMValueRef index = NULL;
|
||||
err = impl_expr(unit, scope, builder, expr->impl.dereference.index, false, &index);
|
||||
if (err.kind != Success) {
|
||||
return err;
|
||||
}
|
||||
|
||||
LLVMValueRef array = NULL;
|
||||
err = impl_storage_expr(unit, builder, scope, expr->impl.dereference.array, &array);
|
||||
if (err.kind != Success) {
|
||||
return err;
|
||||
}
|
||||
|
||||
LLVMTypeRef deref_type = NULL;
|
||||
err = get_type_impl(unit, scope->func_scope->global_scope, expr->target_type, &deref_type);
|
||||
if (err.kind != Success) {
|
||||
return err;
|
||||
}
|
||||
|
||||
if (expr->target_type->kind == TypeKindReference) {
|
||||
array = LLVMBuildLoad2(builder, deref_type, array, "strg.deref.indirect-load");
|
||||
}
|
||||
|
||||
*storage_target = LLVMBuildGEP2(builder, deref_type, array, &index, 1, "strg.deref");
|
||||
|
||||
break;
|
||||
case StorageExprKindBoxAccess:
|
||||
// TODO: resolve LLVMValueRef from BoxAccess
|
||||
break;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
BackendError impl_assign_stmt(
|
||||
LLVMBackendCompileUnit *unit,
|
||||
LLVMBuilderRef
|
||||
builder,
|
||||
LLVMLocalScope *scope,
|
||||
const Assignment *assignment
|
||||
) {
|
||||
BackendError err = SUCCESS;
|
||||
DEBUG("implementing assignment for variable: %p", assignment);
|
||||
|
||||
LLVMValueRef llvm_value = NULL;
|
||||
err = impl_expr(unit, scope, builder, assignment->value, TRUE, &llvm_value);
|
||||
if (err.kind != Success) {
|
||||
return err;
|
||||
}
|
||||
|
||||
LLVMValueRef llvm_array = NULL;
|
||||
err = impl_storage_expr(unit, builder, scope, assignment->destination, &llvm_array);
|
||||
if (err.kind != Success) {
|
||||
return err;
|
||||
}
|
||||
|
||||
LLVMBuildStore(builder, llvm_value, llvm_array);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
BackendError impl_basic_block(LLVMBackendCompileUnit *unit,
|
||||
LLVMBuilderRef builder, LLVMLocalScope *scope,
|
||||
const Block *block, LLVMBasicBlockRef *llvm_start_block, LLVMBasicBlockRef *llvm_end_block) {
|
||||
DEBUG("implementing basic block...");
|
||||
BackendError err = SUCCESS;
|
||||
|
||||
LLVMLocalScope *block_scope = new_local_scope(scope);
|
||||
// append a new LLVM basic block
|
||||
*llvm_start_block = LLVMAppendBasicBlockInContext(unit->context, scope->func_scope->llvm_func,
|
||||
"stmt.block.start");
|
||||
LLVMPositionBuilderAtEnd(builder, *llvm_start_block);
|
||||
|
||||
LLVMBasicBlockRef end_previous_block = *llvm_start_block;
|
||||
|
||||
for (size_t i = 0; i < block->statemnts->len; i++) {
|
||||
DEBUG("building block statement %d of %d", i, block->statemnts->len);
|
||||
Statement* stmt = g_array_index(block->statemnts, Statement*, i);
|
||||
|
||||
LLVMBasicBlockRef llvm_next_start_block = NULL;
|
||||
LLVMBasicBlockRef llvm_next_end_block = NULL;
|
||||
err = impl_stmt(unit, builder, scope, stmt, &llvm_next_start_block, &llvm_next_end_block);
|
||||
if (err.kind != Success) {
|
||||
return err;
|
||||
}
|
||||
|
||||
if (llvm_next_end_block != NULL) {
|
||||
LLVMPositionBuilderAtEnd(builder, end_previous_block);
|
||||
LLVMBuildBr(builder, llvm_next_start_block);
|
||||
LLVMPositionBuilderAtEnd(builder, llvm_next_end_block);
|
||||
end_previous_block = llvm_next_end_block;
|
||||
}
|
||||
}
|
||||
|
||||
*llvm_end_block = end_previous_block;
|
||||
|
||||
delete_local_scope(block_scope);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
BackendError impl_while(LLVMBackendCompileUnit *unit,
|
||||
LLVMBuilderRef builder, LLVMLocalScope *scope,
|
||||
LLVMBasicBlockRef* llvm_start_block,
|
||||
LLVMBasicBlockRef* llvm_end_block,
|
||||
const While *while_stmt) {
|
||||
DEBUG("implementing while...");
|
||||
BackendError err;
|
||||
|
||||
// Create condition block
|
||||
LLVMBasicBlockRef while_cond_block = LLVMAppendBasicBlockInContext(unit->context, scope->func_scope->llvm_func,
|
||||
"loop.while.cond");
|
||||
*llvm_start_block = while_cond_block;
|
||||
LLVMPositionBuilderAtEnd(builder, while_cond_block);
|
||||
// Resolve condition in block to a variable
|
||||
LLVMValueRef cond_result = NULL;
|
||||
err = impl_expr(unit, scope, builder, (Expression *) while_stmt->conditon, FALSE, &cond_result);
|
||||
if (err.kind != Success) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// build body of loop
|
||||
LLVMBasicBlockRef while_start_body_block = NULL;
|
||||
LLVMBasicBlockRef while_end_body_block = NULL;
|
||||
err = impl_basic_block(unit, builder, scope, &while_stmt->block, &while_start_body_block, &while_end_body_block);
|
||||
if (err.kind != Success) {
|
||||
return err;
|
||||
}
|
||||
|
||||
LLVMPositionBuilderAtEnd(builder, while_end_body_block);
|
||||
// jump back to condition after body end
|
||||
LLVMBuildBr(builder, while_cond_block);
|
||||
|
||||
// builder will continue after the loop
|
||||
LLVMBasicBlockRef while_after_block = LLVMAppendBasicBlockInContext(unit->context, scope->func_scope->llvm_func,
|
||||
"loop.while.after");
|
||||
// build conditional branch at end of condition block
|
||||
LLVMPositionBuilderAtEnd(builder, while_cond_block);
|
||||
LLVMBuildCondBr(builder, cond_result, while_start_body_block, while_after_block);
|
||||
|
||||
LLVMPositionBuilderAtEnd(builder, while_after_block);
|
||||
|
||||
*llvm_end_block = while_after_block;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
gboolean is_parameter_out(Parameter *param) {
|
||||
gboolean is_out = FALSE;
|
||||
|
||||
if (param->kind == ParameterDeclarationKind) {
|
||||
is_out = param->impl.declaration.qualifier == Out || param->impl.declaration.qualifier == InOut;
|
||||
} else {
|
||||
is_out = param->impl.definiton.declaration.qualifier == Out ||
|
||||
param->impl.definiton.declaration.qualifier == InOut;
|
||||
}
|
||||
|
||||
return is_out;
|
||||
}
|
||||
|
||||
BackendError impl_func_call(LLVMBackendCompileUnit *unit,
|
||||
LLVMBuilderRef builder, LLVMLocalScope *scope,
|
||||
const FunctionCall *call) {
|
||||
DEBUG("implementing function call...");
|
||||
BackendError err = SUCCESS;
|
||||
|
||||
LLVMValueRef* arguments = mem_alloc(MemoryNamespaceLlvm, sizeof(LLVMValueRef) * call->expressions->len);
|
||||
|
||||
for (size_t i = 0; i < call->expressions->len; i++) {
|
||||
Expression *arg = g_array_index(call->expressions, Expression*, i);
|
||||
|
||||
GArray* param_list;
|
||||
if (call->function->kind == FunctionDeclarationKind) {
|
||||
param_list = call->function->impl.definition.parameter;
|
||||
} else {
|
||||
param_list = call->function->impl.declaration.parameter;
|
||||
}
|
||||
|
||||
LLVMBool reference = FALSE;
|
||||
Parameter parameter = g_array_index(param_list, Parameter, i);
|
||||
if (is_parameter_out(¶meter)) {
|
||||
reference = TRUE;
|
||||
}
|
||||
|
||||
LLVMValueRef llvm_arg = NULL;
|
||||
err = impl_expr(unit, scope, builder, arg, reference, &llvm_arg);
|
||||
|
||||
if (err.kind != Success) {
|
||||
break;
|
||||
}
|
||||
|
||||
arguments[i] = llvm_arg;
|
||||
}
|
||||
|
||||
if (err.kind == Success) {
|
||||
LLVMValueRef llvm_func = LLVMGetNamedFunction(unit->module, call->function->name);
|
||||
|
||||
if (llvm_func == NULL) {
|
||||
return new_backend_impl_error(Implementation, NULL, "no declared function");
|
||||
}
|
||||
|
||||
LLVMTypeRef llvm_func_type = g_hash_table_lookup(scope->func_scope->global_scope->functions, call->function->name);
|
||||
|
||||
LLVMBuildCall2(builder, llvm_func_type, llvm_func, arguments, call->expressions->len,
|
||||
"");
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
BackendError
|
||||
impl_cond_block(LLVMBackendCompileUnit *unit, LLVMBuilderRef builder, LLVMLocalScope *scope, Expression *cond,
|
||||
const Block *block, LLVMBasicBlockRef *cond_block, LLVMBasicBlockRef *start_body_block, LLVMBasicBlockRef *end_body_block,
|
||||
LLVMValueRef *llvm_cond) {
|
||||
BackendError err;
|
||||
|
||||
*cond_block = LLVMAppendBasicBlockInContext(unit->context, scope->func_scope->llvm_func,
|
||||
"stmt.branch.cond");
|
||||
LLVMPositionBuilderAtEnd(builder, *cond_block);
|
||||
// Resolve condition in block to a variable
|
||||
err = impl_expr(unit, scope, builder, cond, FALSE, llvm_cond);
|
||||
if (err.kind == Success) {
|
||||
// build body of loop
|
||||
err = impl_basic_block(unit, builder, scope, block, start_body_block, end_body_block);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
BackendError impl_branch(LLVMBackendCompileUnit *unit,
|
||||
LLVMBuilderRef builder, LLVMLocalScope *scope,
|
||||
LLVMBasicBlockRef* branch_start_block,
|
||||
LLVMBasicBlockRef* branch_end_block,
|
||||
const Branch *branch) {
|
||||
BackendError err = SUCCESS;
|
||||
|
||||
GArray *cond_blocks = g_array_new(FALSE, FALSE, sizeof(LLVMBasicBlockRef));
|
||||
GArray *start_body_blocks = g_array_new(FALSE, FALSE, sizeof(LLVMBasicBlockRef));
|
||||
GArray *end_body_blocks = g_array_new(FALSE, FALSE, sizeof(LLVMBasicBlockRef));
|
||||
GArray *cond_values = g_array_new(FALSE, FALSE, sizeof(LLVMValueRef));
|
||||
|
||||
// add If to arrays
|
||||
{
|
||||
LLVMBasicBlockRef cond_block = NULL;
|
||||
LLVMBasicBlockRef start_body_block = NULL;
|
||||
LLVMBasicBlockRef end_body_block = NULL;
|
||||
LLVMValueRef cond_value = NULL;
|
||||
|
||||
err = impl_cond_block(unit, builder, scope, branch->ifBranch.conditon, &branch->ifBranch.block,
|
||||
&cond_block,
|
||||
&start_body_block, &end_body_block, &cond_value);
|
||||
|
||||
g_array_append_val(cond_blocks, cond_block);
|
||||
g_array_append_val(start_body_blocks, start_body_block);
|
||||
g_array_append_val(end_body_blocks, end_body_block);
|
||||
g_array_append_val(cond_values, cond_value);
|
||||
}
|
||||
|
||||
// generate else if(s)
|
||||
if (branch->elseIfBranches != NULL) {
|
||||
for (size_t i = 0; i < branch->elseIfBranches->len; i++) {
|
||||
LLVMBasicBlockRef cond_block = NULL;
|
||||
LLVMBasicBlockRef start_body_block = NULL;
|
||||
LLVMBasicBlockRef end_body_block = NULL;
|
||||
LLVMValueRef cond_value = NULL;
|
||||
|
||||
ElseIf *elseIf = ((ElseIf *) branch->elseIfBranches->data) + i;
|
||||
|
||||
err = impl_cond_block(unit, builder, scope, elseIf->conditon, &elseIf->block, &cond_block,
|
||||
&start_body_block, &end_body_block, &cond_value);
|
||||
|
||||
g_array_append_val(cond_blocks, cond_block);
|
||||
g_array_append_val(start_body_blocks, start_body_block);
|
||||
g_array_append_val(end_body_blocks, end_body_block);
|
||||
g_array_append_val(cond_values, cond_value);
|
||||
}
|
||||
}
|
||||
|
||||
LLVMBasicBlockRef after_block = NULL;
|
||||
|
||||
// else block
|
||||
if (branch->elseBranch.block.statemnts != NULL) {
|
||||
LLVMBasicBlockRef start_else_block = NULL;
|
||||
err = impl_basic_block(unit, builder, scope, &branch->elseBranch.block, &start_else_block, &after_block);
|
||||
g_array_append_val(cond_blocks, start_else_block);
|
||||
}
|
||||
|
||||
if (after_block == NULL) {
|
||||
after_block = LLVMAppendBasicBlockInContext(unit->context, scope->func_scope->llvm_func,
|
||||
"stmt.branch.after");
|
||||
}
|
||||
|
||||
LLVMPositionBuilderAtEnd(builder, after_block);
|
||||
// in case no else block is present
|
||||
// make the after block the else
|
||||
if (branch->elseBranch.block.statemnts == NULL) {
|
||||
g_array_append_val(cond_blocks, after_block);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < cond_blocks->len - 1; i++) {
|
||||
LLVMBasicBlockRef next_block = g_array_index(cond_blocks, LLVMBasicBlockRef, i + 1);
|
||||
LLVMBasicBlockRef cond_block = g_array_index(cond_blocks, LLVMBasicBlockRef, i);
|
||||
LLVMBasicBlockRef start_body_block = g_array_index(start_body_blocks, LLVMBasicBlockRef, i);
|
||||
LLVMBasicBlockRef end_body_block = g_array_index(end_body_blocks, LLVMBasicBlockRef, i);
|
||||
LLVMValueRef cond_value = g_array_index(cond_values, LLVMValueRef, i);
|
||||
|
||||
LLVMPositionBuilderAtEnd(builder, cond_block);
|
||||
LLVMBuildCondBr(builder, cond_value, start_body_block, next_block);
|
||||
|
||||
LLVMPositionBuilderAtEnd(builder, end_body_block);
|
||||
LLVMBuildBr(builder, after_block);
|
||||
}
|
||||
|
||||
*branch_start_block = g_array_index(cond_blocks, LLVMBasicBlockRef, 0);
|
||||
*branch_end_block = after_block;
|
||||
|
||||
g_array_free(cond_blocks, TRUE);
|
||||
g_array_free(start_body_blocks, TRUE);
|
||||
g_array_free(end_body_blocks, TRUE);
|
||||
g_array_free(cond_values, TRUE);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
BackendError impl_decl(LLVMBackendCompileUnit *unit,
|
||||
LLVMBuilderRef builder,
|
||||
LLVMLocalScope *scope,
|
||||
VariableDeclaration *decl,
|
||||
const char *name) {
|
||||
DEBUG("implementing local declaration: %s", name);
|
||||
BackendError err = SUCCESS;
|
||||
LLVMTypeRef llvm_type = NULL;
|
||||
err = get_type_impl(unit, scope->func_scope->global_scope, decl->type, &llvm_type);
|
||||
|
||||
if (err.kind != Success) {
|
||||
return err;
|
||||
}
|
||||
|
||||
DEBUG("creating local variable...");
|
||||
LLVMValueRef local = LLVMBuildAlloca(builder, llvm_type, name);
|
||||
|
||||
LLVMValueRef initial_value = NULL;
|
||||
err = get_type_default_value(unit, scope->func_scope->global_scope, decl->type, &initial_value);
|
||||
|
||||
if (err.kind == Success) {
|
||||
DEBUG("setting default value...");
|
||||
LLVMBuildStore(builder, initial_value, local);
|
||||
g_hash_table_insert(scope->vars, (gpointer) name, local);
|
||||
} else {
|
||||
ERROR("unable to initialize local variable: %s", err.impl.message);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
BackendError impl_def(LLVMBackendCompileUnit *unit,
|
||||
LLVMBuilderRef builder,
|
||||
LLVMLocalScope *scope,
|
||||
VariableDefiniton *def,
|
||||
const char *name) {
|
||||
DEBUG("implementing local definition: %s", name);
|
||||
BackendError err = SUCCESS;
|
||||
LLVMTypeRef llvm_type = NULL;
|
||||
err = get_type_impl(unit, scope->func_scope->global_scope, def->declaration.type, &llvm_type);
|
||||
|
||||
if (err.kind != Success) {
|
||||
return err;
|
||||
}
|
||||
|
||||
LLVMValueRef initial_value = NULL;
|
||||
err = impl_expr(unit, scope, builder, def->initializer, FALSE, &initial_value);
|
||||
if (err.kind != Success) {
|
||||
return err;
|
||||
}
|
||||
|
||||
DEBUG("creating local variable...");
|
||||
LLVMValueRef local = LLVMBuildAlloca(builder, llvm_type, name);
|
||||
|
||||
DEBUG("setting default value");
|
||||
LLVMBuildStore(builder, initial_value, local);
|
||||
g_hash_table_insert(scope->vars, (gpointer) name, local);
|
||||
return err;
|
||||
}
|
||||
|
||||
BackendError impl_var(LLVMBackendCompileUnit *unit,
|
||||
LLVMBuilderRef builder,
|
||||
LLVMLocalScope *scope,
|
||||
Variable *var) {
|
||||
BackendError err;
|
||||
|
||||
switch (var->kind) {
|
||||
case VariableKindDeclaration:
|
||||
err = impl_decl(unit, builder, scope, &var->impl.declaration, var->name);
|
||||
break;
|
||||
case VariableKindDefinition:
|
||||
err = impl_def(unit, builder, scope, &var->impl.definiton, var->name);
|
||||
break;
|
||||
default:
|
||||
err = new_backend_impl_error(Implementation, NULL, "Unexpected variable kind in statement");
|
||||
break;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
BackendError impl_stmt(LLVMBackendCompileUnit *unit, LLVMBuilderRef builder, LLVMLocalScope *scope, Statement *stmt, LLVMBasicBlockRef* llvm_start_block, LLVMBasicBlockRef* llvm_end_block) {
|
||||
assert(stmt != NULL);
|
||||
DEBUG("implementing statement: %ld", stmt->kind);
|
||||
BackendError err;
|
||||
|
||||
switch (stmt->kind) {
|
||||
case StatementKindAssignment:
|
||||
err = impl_assign_stmt(unit, builder, scope, &stmt->impl.assignment);
|
||||
break;
|
||||
case StatementKindBranch:
|
||||
err = impl_branch(unit, builder, scope, llvm_start_block, llvm_end_block, &stmt->impl.branch);
|
||||
break;
|
||||
case StatementKindDeclaration:
|
||||
case StatementKindDefinition:
|
||||
err = impl_var(unit, builder, scope, stmt->impl.variable);
|
||||
break;
|
||||
case StatementKindWhile:
|
||||
err = impl_while(unit, builder, scope, llvm_start_block, llvm_end_block, &stmt->impl.whileLoop);
|
||||
break;
|
||||
case StatementKindFunctionCall:
|
||||
err = impl_func_call(unit, builder, scope, &stmt->impl.call);
|
||||
break;
|
||||
default:
|
||||
err = new_backend_impl_error(Implementation, NULL, "Unexpected statement kind");
|
||||
break;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
BackendError impl_block(LLVMBackendCompileUnit *unit,
|
||||
LLVMBuilderRef builder, LLVMFuncScope *scope,
|
||||
LLVMBasicBlockRef* llvm_start_block,
|
||||
LLVMBasicBlockRef* llvm_end_block,
|
||||
const Block *block) {
|
||||
DEBUG("Implementing function block...");
|
||||
BackendError err = SUCCESS;
|
||||
|
||||
LLVMLocalScope *function_entry_scope = malloc(sizeof(LLVMLocalScope));
|
||||
function_entry_scope->func_scope = scope;
|
||||
function_entry_scope->vars = g_hash_table_new(g_str_hash, g_str_equal);
|
||||
function_entry_scope->parent_scope = NULL;
|
||||
|
||||
err = impl_basic_block(unit, builder, function_entry_scope, block, llvm_start_block, llvm_end_block);
|
||||
|
||||
delete_local_scope(function_entry_scope);
|
||||
|
||||
return err;
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
//
|
||||
// Created by servostar on 5/28/24.
|
||||
//
|
||||
|
||||
#ifndef LLVM_BACKEND_STMT_H
|
||||
#define LLVM_BACKEND_STMT_H
|
||||
|
||||
#include <llvm/llvm-ir/func.h>
|
||||
|
||||
BackendError impl_block(LLVMBackendCompileUnit *unit,
|
||||
LLVMBuilderRef builder, LLVMFuncScope *scope,
|
||||
LLVMBasicBlockRef* llvm_start_block,
|
||||
LLVMBasicBlockRef* llvm_end_block,
|
||||
const Block *block);
|
||||
|
||||
BackendError impl_stmt(LLVMBackendCompileUnit *unit, LLVMBuilderRef builder, LLVMLocalScope *scope, Statement *stmt,
|
||||
LLVMBasicBlockRef *llvm_start_block, LLVMBasicBlockRef *llvm_end_block);
|
||||
|
||||
#endif // LLVM_BACKEND_STMT_H
|
|
@ -0,0 +1,461 @@
|
|||
#include <codegen/backend.h>
|
||||
#include <llvm-c/Core.h>
|
||||
#include <llvm-c/Types.h>
|
||||
#include <llvm/llvm-ir/types.h>
|
||||
#include <llvm/parser.h>
|
||||
#include <set/types.h>
|
||||
#include <sys/log.h>
|
||||
#include <set/set.h>
|
||||
#include <stdlib.h>
|
||||
#include <mem/cache.h>
|
||||
|
||||
#define BASE_BYTES 4
|
||||
#define BITS_PER_BYTE 8
|
||||
|
||||
char* guid() {
|
||||
return "uuid";
|
||||
}
|
||||
|
||||
static BackendError get_const_primitive_value(PrimitiveType primitive,
|
||||
LLVMTypeRef llvm_type,
|
||||
const char* value,
|
||||
LLVMValueRef* llvm_value) {
|
||||
switch (primitive) {
|
||||
case Int:
|
||||
*llvm_value = LLVMConstIntOfString(llvm_type, value, 10);
|
||||
break;
|
||||
case Float:
|
||||
*llvm_value = LLVMConstRealOfString(llvm_type, value);
|
||||
break;
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
static BackendError get_const_composite_value(CompositeType composite,
|
||||
LLVMTypeRef llvm_type,
|
||||
const char* value,
|
||||
LLVMValueRef* llvm_value) {
|
||||
return get_const_primitive_value(composite.primitive, llvm_type, value,
|
||||
llvm_value);
|
||||
}
|
||||
|
||||
BackendError impl_reference_const(LLVMBackendCompileUnit* unit, TypeValue* value, LLVMValueRef* llvm_value) {
|
||||
BackendError err = SUCCESS;
|
||||
if (value->type->kind == TypeKindReference && compareTypes(value->type, (Type*) &StringLiteralType)) {
|
||||
// is string literal
|
||||
LLVMValueRef string_value = LLVMConstString(value->value, strlen(value->value), false);
|
||||
|
||||
char uuid[9];
|
||||
sprintf(uuid, "%08x", g_str_hash(value->value));
|
||||
|
||||
LLVMValueRef string_global = LLVMAddGlobal(unit->module, LLVMTypeOf(string_value), uuid);
|
||||
LLVMSetInitializer(string_global, string_value);
|
||||
LLVMSetGlobalConstant(string_global, true);
|
||||
LLVMSetUnnamedAddress(string_global, LLVMGlobalUnnamedAddr);
|
||||
LLVMSetAlignment(string_global, 1);
|
||||
|
||||
*llvm_value = string_global;
|
||||
} else {
|
||||
err = new_backend_impl_error(Implementation, value->nodePtr, "reference initializer can only be string literals");
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
BackendError get_const_type_value(LLVMBackendCompileUnit* unit,
|
||||
LLVMGlobalScope* scope,
|
||||
TypeValue* gemstone_value,
|
||||
LLVMValueRef* llvm_value) {
|
||||
BackendError err;
|
||||
|
||||
LLVMTypeRef llvm_type = NULL;
|
||||
err = get_type_impl(unit, scope, gemstone_value->type, &llvm_type);
|
||||
if (err.kind != Success) {
|
||||
return err;
|
||||
}
|
||||
|
||||
switch (gemstone_value->type->kind) {
|
||||
case TypeKindPrimitive:
|
||||
err = get_const_primitive_value(gemstone_value->type->impl.primitive,
|
||||
llvm_type, gemstone_value->value,
|
||||
llvm_value);
|
||||
break;
|
||||
case TypeKindComposite:
|
||||
err = get_const_composite_value(gemstone_value->type->impl.composite,
|
||||
llvm_type, gemstone_value->value,
|
||||
llvm_value);
|
||||
break;
|
||||
case TypeKindReference:
|
||||
err = impl_reference_const(unit, gemstone_value, llvm_value);
|
||||
break;
|
||||
case TypeKindBox:
|
||||
err =
|
||||
new_backend_impl_error(Implementation, gemstone_value->nodePtr,
|
||||
"boxes cannot be constant value");
|
||||
break;
|
||||
default:
|
||||
PANIC("invalid value kind: %ld", gemstone_value->type->kind);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
BackendError impl_primtive_type(LLVMBackendCompileUnit* unit,
|
||||
PrimitiveType primtive,
|
||||
LLVMTypeRef* llvm_type) {
|
||||
switch (primtive) {
|
||||
case Int:
|
||||
DEBUG("implementing primtive integral type...");
|
||||
*llvm_type = LLVMInt32TypeInContext(unit->context);
|
||||
break;
|
||||
case Float:
|
||||
DEBUG("implementing primtive float type...");
|
||||
*llvm_type = LLVMFloatTypeInContext(unit->context);
|
||||
break;
|
||||
default:
|
||||
PANIC("invalid primitive type");
|
||||
break;
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
BackendError impl_integral_type(LLVMBackendCompileUnit* unit, Scale scale,
|
||||
LLVMTypeRef* llvm_type) {
|
||||
size_t bits = (int)(BASE_BYTES * scale) * BITS_PER_BYTE;
|
||||
DEBUG("implementing integral type of size: %ld", bits);
|
||||
LLVMTypeRef integral_type = LLVMIntTypeInContext(unit->context, bits);
|
||||
|
||||
*llvm_type = integral_type;
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
BackendError impl_float_type(LLVMBackendCompileUnit* unit, Scale scale,
|
||||
LLVMTypeRef* llvm_type) {
|
||||
DEBUG("implementing floating point...");
|
||||
LLVMTypeRef float_type = NULL;
|
||||
|
||||
size_t bytes = (int)(scale * BASE_BYTES);
|
||||
DEBUG("requested float of bytes: %ld", bytes);
|
||||
switch (bytes) {
|
||||
case 2:
|
||||
float_type = LLVMHalfTypeInContext(unit->context);
|
||||
break;
|
||||
case 4:
|
||||
float_type = LLVMFloatTypeInContext(unit->context);
|
||||
break;
|
||||
case 8:
|
||||
float_type = LLVMDoubleTypeInContext(unit->context);
|
||||
break;
|
||||
case 16:
|
||||
float_type = LLVMFP128TypeInContext(unit->context);
|
||||
break;
|
||||
default:
|
||||
ERROR("invalid floating point size: %ld bit",
|
||||
bytes * BITS_PER_BYTE);
|
||||
break;
|
||||
}
|
||||
|
||||
if (float_type != NULL) {
|
||||
*llvm_type = float_type;
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
return new_backend_impl_error(Implementation, NULL,
|
||||
"floating point with scale not supported");
|
||||
}
|
||||
|
||||
BackendError impl_composite_type(LLVMBackendCompileUnit* unit,
|
||||
CompositeType* composite,
|
||||
LLVMTypeRef* llvm_type) {
|
||||
DEBUG("implementing composite type...");
|
||||
BackendError err = SUCCESS;
|
||||
|
||||
switch (composite->primitive) {
|
||||
case Int:
|
||||
err = impl_integral_type(unit, composite->scale, llvm_type);
|
||||
break;
|
||||
case Float:
|
||||
if (composite->sign == Signed) {
|
||||
err = impl_float_type(unit, composite->scale, llvm_type);
|
||||
} else {
|
||||
ERROR("unsigned floating point not supported");
|
||||
err = new_backend_impl_error(
|
||||
Implementation, composite->nodePtr,
|
||||
"unsigned floating-point not supported");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
PANIC("invalid primitive kind: %ld", composite->primitive);
|
||||
break;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
BackendError impl_reference_type(LLVMBackendCompileUnit* unit,
|
||||
LLVMGlobalScope* scope,
|
||||
ReferenceType reference,
|
||||
LLVMTypeRef* llvm_type);
|
||||
|
||||
BackendError impl_box_type(LLVMBackendCompileUnit* unit, LLVMGlobalScope* scope,
|
||||
BoxType* reference, LLVMTypeRef* llvm_type);
|
||||
|
||||
BackendError get_type_impl(LLVMBackendCompileUnit* unit, LLVMGlobalScope* scope,
|
||||
Type* gemstone_type, LLVMTypeRef* llvm_type) {
|
||||
DEBUG("retrieving type implementation...");
|
||||
BackendError err;
|
||||
|
||||
switch (gemstone_type->kind) {
|
||||
case TypeKindPrimitive:
|
||||
err = impl_primtive_type(unit, gemstone_type->impl.primitive,
|
||||
llvm_type);
|
||||
break;
|
||||
case TypeKindComposite:
|
||||
err = impl_composite_type(unit, &gemstone_type->impl.composite,
|
||||
llvm_type);
|
||||
break;
|
||||
case TypeKindReference:
|
||||
err = impl_reference_type(unit, scope,
|
||||
gemstone_type->impl.reference, llvm_type);
|
||||
break;
|
||||
case TypeKindBox:
|
||||
err =
|
||||
impl_box_type(unit, scope, gemstone_type->impl.box, llvm_type);
|
||||
break;
|
||||
default:
|
||||
PANIC("invalid type kind: %ld", gemstone_type->kind);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
BackendError impl_box_type(LLVMBackendCompileUnit* unit, LLVMGlobalScope* scope,
|
||||
BoxType* box, LLVMTypeRef* llvm_type) {
|
||||
DEBUG("implementing box type...");
|
||||
GHashTableIter iterator;
|
||||
g_hash_table_iter_init(&iterator, box->member);
|
||||
|
||||
gpointer key = NULL;
|
||||
gpointer val = NULL;
|
||||
|
||||
BackendError err;
|
||||
|
||||
GArray* members = g_array_new(FALSE, FALSE, sizeof(LLVMTypeRef));
|
||||
|
||||
DEBUG("implementing box members...");
|
||||
while (g_hash_table_iter_next(&iterator, &key, &val) != FALSE) {
|
||||
Type* member_type = ((BoxMember*)val)->type;
|
||||
|
||||
DEBUG("implementing member: %s ", ((BoxMember*)val)->name);
|
||||
|
||||
LLVMTypeRef llvm_local_type = NULL;
|
||||
err = get_type_impl(unit, scope, member_type, &llvm_local_type);
|
||||
|
||||
if (err.kind != Success) {
|
||||
break;
|
||||
}
|
||||
|
||||
g_array_append_val(members, llvm_type);
|
||||
}
|
||||
DEBUG("implemented %ld members", members->len);
|
||||
|
||||
if (err.kind == Success) {
|
||||
*llvm_type =
|
||||
LLVMStructType((LLVMTypeRef*)members->data, members->len, 0);
|
||||
}
|
||||
|
||||
g_array_free(members, FALSE);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
BackendError impl_reference_type(LLVMBackendCompileUnit* unit,
|
||||
LLVMGlobalScope* scope,
|
||||
ReferenceType reference,
|
||||
LLVMTypeRef* llvm_type) {
|
||||
DEBUG("implementing reference type...");
|
||||
BackendError err = SUCCESS;
|
||||
LLVMTypeRef type = NULL;
|
||||
err = get_type_impl(unit, scope, reference, &type);
|
||||
|
||||
if (err.kind == Success) {
|
||||
*llvm_type = LLVMPointerType(type, 0);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
BackendError impl_type(LLVMBackendCompileUnit* unit, Type* gemstone_type,
|
||||
const char* alias, LLVMGlobalScope* scope) {
|
||||
BackendError err = SUCCESS;
|
||||
DEBUG("implementing type of kind: %ld as %s", gemstone_type->kind, alias);
|
||||
|
||||
LLVMTypeRef llvm_type = NULL;
|
||||
err = get_type_impl(unit, scope, gemstone_type, &llvm_type);
|
||||
|
||||
if (err.kind == Success) {
|
||||
g_hash_table_insert(scope->types, (gpointer)alias, llvm_type);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
BackendError impl_type_define(LLVMBackendCompileUnit* unit, Typedefine* gemstone_type,
|
||||
const char* alias, LLVMGlobalScope* scope) {
|
||||
BackendError err = SUCCESS;
|
||||
DEBUG("implementing type of kind: %ld as %s", gemstone_type->type->kind, alias);
|
||||
|
||||
err = impl_type(unit, gemstone_type->type, alias, scope);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
BackendError impl_types(LLVMBackendCompileUnit* unit, LLVMGlobalScope* scope,
|
||||
GHashTable* types) {
|
||||
DEBUG("implementing given types of %p", types);
|
||||
GHashTableIter iterator;
|
||||
g_hash_table_iter_init(&iterator, types);
|
||||
|
||||
gpointer key = NULL;
|
||||
gpointer val = NULL;
|
||||
|
||||
BackendError err;
|
||||
|
||||
while (g_hash_table_iter_next(&iterator, &key, &val) != FALSE) {
|
||||
err = impl_type_define(unit, (Typedefine*) val, (const char*)key, scope);
|
||||
|
||||
if (err.kind != Success) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
BackendError get_primitive_default_value(PrimitiveType type,
|
||||
LLVMTypeRef llvm_type,
|
||||
LLVMValueRef* llvm_value) {
|
||||
DEBUG("building primitive %ld default value", type);
|
||||
switch (type) {
|
||||
case Int:
|
||||
*llvm_value = LLVMConstIntOfString(llvm_type, "0", 10);
|
||||
break;
|
||||
case Float:
|
||||
*llvm_value = LLVMConstRealOfString(llvm_type, "0");
|
||||
break;
|
||||
default:
|
||||
ERROR("invalid primitive type: %ld", type);
|
||||
return new_backend_impl_error(Implementation, NULL,
|
||||
"unknown primitive type");
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
BackendError get_composite_default_value(CompositeType* composite,
|
||||
LLVMTypeRef llvm_type,
|
||||
LLVMValueRef* llvm_value) {
|
||||
DEBUG("building composite default value");
|
||||
BackendError err = SUCCESS;
|
||||
|
||||
switch (composite->primitive) {
|
||||
case Int:
|
||||
err = get_primitive_default_value(Int, llvm_type, llvm_value);
|
||||
break;
|
||||
case Float:
|
||||
if (composite->sign == Signed) {
|
||||
err = get_primitive_default_value(Float, llvm_type, llvm_value);
|
||||
} else {
|
||||
err = new_backend_impl_error(
|
||||
Implementation, composite->nodePtr,
|
||||
"unsigned floating-point not supported");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ERROR("invalid primitive type: %ld", composite->primitive);
|
||||
break;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
BackendError get_reference_default_value(LLVMTypeRef llvm_type,
|
||||
LLVMValueRef* llvm_value) {
|
||||
DEBUG("building reference default value");
|
||||
*llvm_value = LLVMConstPointerNull(llvm_type);
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
BackendError get_box_default_value(LLVMBackendCompileUnit* unit,
|
||||
LLVMGlobalScope* scope, BoxType* type,
|
||||
LLVMTypeRef llvm_type,
|
||||
LLVMValueRef* llvm_value) {
|
||||
DEBUG("building box default value...");
|
||||
GHashTableIter iterator;
|
||||
g_hash_table_iter_init(&iterator, type->member);
|
||||
|
||||
gpointer key = NULL;
|
||||
gpointer val = NULL;
|
||||
|
||||
BackendError err;
|
||||
|
||||
GArray* constants = g_array_new(FALSE, FALSE, sizeof(LLVMValueRef));
|
||||
|
||||
while (g_hash_table_iter_next(&iterator, &key, &val) != FALSE) {
|
||||
Type* member_type = ((BoxMember*)val)->type;
|
||||
|
||||
LLVMValueRef constant = NULL;
|
||||
err = get_type_default_value(unit, scope, member_type, &constant);
|
||||
|
||||
if (err.kind != Success) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG("build %ld member default values", constants->len);
|
||||
|
||||
*llvm_value = LLVMConstNamedStruct(
|
||||
llvm_type, (LLVMValueRef*)constants->data, constants->len);
|
||||
|
||||
g_array_free(constants, FALSE);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
BackendError get_type_default_value(LLVMBackendCompileUnit* unit,
|
||||
LLVMGlobalScope* scope, Type* gemstone_type,
|
||||
LLVMValueRef* llvm_value) {
|
||||
BackendError err;
|
||||
|
||||
LLVMTypeRef llvm_type = NULL;
|
||||
err = get_type_impl(unit, scope, gemstone_type, &llvm_type);
|
||||
if (err.kind != Success) {
|
||||
return err;
|
||||
}
|
||||
|
||||
switch (gemstone_type->kind) {
|
||||
case TypeKindPrimitive:
|
||||
err = get_primitive_default_value(gemstone_type->impl.primitive,
|
||||
llvm_type, llvm_value);
|
||||
break;
|
||||
case TypeKindComposite:
|
||||
err = get_composite_default_value(&gemstone_type->impl.composite,
|
||||
llvm_type, llvm_value);
|
||||
break;
|
||||
case TypeKindReference:
|
||||
err = get_reference_default_value(llvm_type, llvm_value);
|
||||
break;
|
||||
case TypeKindBox:
|
||||
err = get_box_default_value(unit, scope, gemstone_type->impl.box,
|
||||
llvm_type, llvm_value);
|
||||
break;
|
||||
default:
|
||||
PANIC("invalid type kind: %ld", gemstone_type->kind);
|
||||
break;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
|
||||
#ifndef LLVM_BACKEND_TYPES_H_
|
||||
#define LLVM_BACKEND_TYPES_H_
|
||||
|
||||
#include <codegen/backend.h>
|
||||
#include <llvm-c/Core.h>
|
||||
#include <llvm-c/Types.h>
|
||||
#include <llvm/parser.h>
|
||||
#include <set/types.h>
|
||||
|
||||
BackendError impl_types(LLVMBackendCompileUnit* unit, LLVMGlobalScope* scope,
|
||||
GHashTable* types);
|
||||
|
||||
BackendError get_type_impl(LLVMBackendCompileUnit* unit, LLVMGlobalScope* scope,
|
||||
Type* gemstone_type, LLVMTypeRef* llvm_type);
|
||||
|
||||
BackendError get_type_default_value(LLVMBackendCompileUnit* unit,
|
||||
LLVMGlobalScope* scope, Type* gemstone_type,
|
||||
LLVMValueRef* llvm_value);
|
||||
|
||||
BackendError get_const_type_value(LLVMBackendCompileUnit* unit,
|
||||
LLVMGlobalScope* scope,
|
||||
TypeValue* gemstone_value,
|
||||
LLVMValueRef* llvm_value);
|
||||
|
||||
#endif // LLVM_BACKEND_TYPES_H_
|
|
@ -0,0 +1,130 @@
|
|||
|
||||
#include <codegen/backend.h>
|
||||
#include <set/types.h>
|
||||
#include <sys/log.h>
|
||||
#include <llvm/llvm-ir/variables.h>
|
||||
#include <llvm-c/Core.h>
|
||||
#include <llvm-c/Types.h>
|
||||
#include <llvm/llvm-ir/types.h>
|
||||
|
||||
BackendError impl_global_declaration(LLVMBackendCompileUnit* unit,
|
||||
LLVMGlobalScope* scope,
|
||||
VariableDeclaration* decl,
|
||||
const char* name) {
|
||||
DEBUG("implementing global declaration: %s", name);
|
||||
BackendError err = SUCCESS;
|
||||
LLVMTypeRef llvm_type = NULL;
|
||||
err = get_type_impl(unit, scope, decl->type, &llvm_type);
|
||||
|
||||
if (err.kind != Success) {
|
||||
return err;
|
||||
}
|
||||
|
||||
DEBUG("creating global variable...");
|
||||
LLVMValueRef global = LLVMAddGlobal(unit->module, llvm_type, name);
|
||||
|
||||
LLVMValueRef initial_value = NULL;
|
||||
err = get_type_default_value(unit, scope, decl->type, &initial_value);
|
||||
|
||||
if (err.kind == Success) {
|
||||
DEBUG("setting default value...");
|
||||
LLVMSetInitializer(global, initial_value);
|
||||
g_hash_table_insert(scope->variables, (gpointer)name, global);
|
||||
} else {
|
||||
ERROR("unable to initialize global variable: %s", err.impl.message);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
BackendError impl_global_definiton(LLVMBackendCompileUnit* unit,
|
||||
LLVMGlobalScope* scope,
|
||||
VariableDefiniton* def, const char* name) {
|
||||
DEBUG("implementing global definition: %s", name);
|
||||
BackendError err = SUCCESS;
|
||||
LLVMTypeRef llvm_type = NULL;
|
||||
err = get_type_impl(unit, scope, def->declaration.type, &llvm_type);
|
||||
|
||||
if (err.kind != Success) {
|
||||
return err;
|
||||
}
|
||||
|
||||
DEBUG("creating global variable...");
|
||||
LLVMValueRef global = LLVMAddGlobal(unit->module, llvm_type, name);
|
||||
|
||||
// FIXME: resolve initializer expression!
|
||||
LLVMValueRef initial_value = NULL;
|
||||
err = get_type_default_value(unit, scope, def->declaration.type,
|
||||
&initial_value);
|
||||
|
||||
if (err.kind == Success) {
|
||||
DEBUG("setting default value");
|
||||
LLVMSetInitializer(global, initial_value);
|
||||
g_hash_table_insert(scope->variables, (gpointer)name, global);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
BackendError impl_global_variable(LLVMBackendCompileUnit* unit,
|
||||
Variable* gemstone_var, const char* alias,
|
||||
LLVMGlobalScope* scope) {
|
||||
DEBUG("implementing global variable: %s", alias);
|
||||
BackendError err = SUCCESS;
|
||||
|
||||
switch (gemstone_var->kind) {
|
||||
case VariableKindDeclaration:
|
||||
err = impl_global_declaration(
|
||||
unit, scope, &gemstone_var->impl.declaration, alias);
|
||||
break;
|
||||
case VariableKindDefinition:
|
||||
err = impl_global_definiton(unit, scope,
|
||||
&gemstone_var->impl.definiton, alias);
|
||||
break;
|
||||
case VariableKindBoxMember:
|
||||
err = new_backend_impl_error(Implementation, gemstone_var->nodePtr,
|
||||
"member variable cannot be ");
|
||||
break;
|
||||
default:
|
||||
PANIC("invalid variable kind: %ld", gemstone_var->kind);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
BackendError impl_global_variables(LLVMBackendCompileUnit* unit,
|
||||
LLVMGlobalScope* scope,
|
||||
GHashTable* variables) {
|
||||
DEBUG("implementing global variables...");
|
||||
GHashTableIter iterator;
|
||||
g_hash_table_iter_init(&iterator, variables);
|
||||
|
||||
gpointer key = NULL;
|
||||
gpointer val = NULL;
|
||||
|
||||
BackendError err = SUCCESS;
|
||||
|
||||
size_t variable_count = 0;
|
||||
while (g_hash_table_iter_next(&iterator, &key, &val) != FALSE) {
|
||||
err =
|
||||
impl_global_variable(unit, (Variable*)val, (const char*)key, scope);
|
||||
|
||||
if (err.kind != Success) {
|
||||
break;
|
||||
}
|
||||
|
||||
variable_count++;
|
||||
}
|
||||
|
||||
DEBUG("implemented %ld global variables", variable_count);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
LLVMValueRef get_global_variable(LLVMGlobalScope* scope, char* name) {
|
||||
if (g_hash_table_contains(scope->variables, name)) {
|
||||
return g_hash_table_lookup(scope->variables, name);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
|
||||
#ifndef LLVM_BACKEND_VARIABLES_H_
|
||||
#define LLVM_BACKEND_VARIABLES_H_
|
||||
|
||||
#include <codegen/backend.h>
|
||||
#include <llvm-c/Core.h>
|
||||
#include <llvm-c/Types.h>
|
||||
#include <llvm/parser.h>
|
||||
#include <set/types.h>
|
||||
|
||||
BackendError impl_global_variables(LLVMBackendCompileUnit* unit,
|
||||
LLVMGlobalScope* scope,
|
||||
GHashTable* variables);
|
||||
|
||||
LLVMValueRef get_global_variable(LLVMGlobalScope* scope, char* name);
|
||||
|
||||
#endif // LLVM_BACKEND_VARIABLES_H_
|
|
@ -0,0 +1,299 @@
|
|||
|
||||
#include <codegen/backend.h>
|
||||
#include <llvm-c/Core.h>
|
||||
#include <llvm-c/Target.h>
|
||||
#include <llvm-c/TargetMachine.h>
|
||||
#include <llvm-c/Types.h>
|
||||
#include <llvm-c/Analysis.h>
|
||||
#include <llvm/backend.h>
|
||||
#include <llvm/parser.h>
|
||||
#include <llvm/llvm-ir/types.h>
|
||||
#include <llvm/llvm-ir/variables.h>
|
||||
#include <llvm/llvm-ir/func.h>
|
||||
#include <set/types.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/log.h>
|
||||
#include <llvm/link/lld.h>
|
||||
|
||||
BackendError export_IR(LLVMBackendCompileUnit* unit, const Target* target,
|
||||
const TargetConfig* config) {
|
||||
DEBUG("exporting module to LLVM-IR...");
|
||||
|
||||
BackendError err = SUCCESS;
|
||||
|
||||
// convert module to LLVM-IR
|
||||
char* ir = LLVMPrintModuleToString(unit->module);
|
||||
|
||||
char* basename = g_strjoin(".", target->name.str, "ll", NULL);
|
||||
// construct file name
|
||||
const char* filename = g_build_filename(config->archive_directory, basename, NULL);
|
||||
|
||||
INFO("Writing LLVM-IR to %s", filename);
|
||||
|
||||
DEBUG("opening file...");
|
||||
FILE* output = fopen(filename, "w");
|
||||
if (output == NULL) {
|
||||
ERROR("unable to open file: %s", filename);
|
||||
err = new_backend_impl_error(Implementation, NULL,
|
||||
"unable to open file for writing");
|
||||
LLVMDisposeMessage(ir);
|
||||
}
|
||||
|
||||
DEBUG("printing LLVM-IR to file...");
|
||||
|
||||
size_t bytes = fprintf(output, "%s", ir);
|
||||
|
||||
// flush and close output file
|
||||
fflush(output);
|
||||
fclose(output);
|
||||
|
||||
INFO("%ld bytes written to %s", bytes, filename);
|
||||
|
||||
g_free((char*)filename);
|
||||
g_free(basename);
|
||||
|
||||
// clean up LLVM-IR string
|
||||
LLVMDisposeMessage(ir);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
BackendError emit_module_to_file(LLVMBackendCompileUnit* unit,
|
||||
LLVMTargetMachineRef target_machine,
|
||||
LLVMCodeGenFileType file_type, char* error,
|
||||
const TargetConfig* config) {
|
||||
BackendError err = SUCCESS;
|
||||
DEBUG("Generating code...");
|
||||
|
||||
const char* basename;
|
||||
const char* filename;
|
||||
switch (file_type) {
|
||||
case LLVMAssemblyFile:
|
||||
basename = g_strjoin(".", config->name, "s", NULL);
|
||||
filename = g_build_filename(config->archive_directory, basename, NULL);
|
||||
break;
|
||||
case LLVMObjectFile:
|
||||
basename = g_strjoin(".", config->name, "o", NULL);
|
||||
filename = g_build_filename(config->archive_directory, basename, NULL);
|
||||
break;
|
||||
default:
|
||||
return new_backend_impl_error(Implementation, NULL,
|
||||
"invalid codegen file");
|
||||
}
|
||||
|
||||
INFO("export to file: %s", filename);
|
||||
|
||||
if (LLVMTargetMachineEmitToFile(target_machine, unit->module, filename,
|
||||
file_type, &error) != 0) {
|
||||
ERROR("failed to emit code: %s", error);
|
||||
err =
|
||||
new_backend_impl_error(Implementation, NULL, "failed to emit code");
|
||||
LLVMDisposeMessage(error);
|
||||
} else {
|
||||
print_message(Info, "Generated code was written to: %s", filename);
|
||||
}
|
||||
|
||||
g_free((void*) filename);
|
||||
g_free((void*) basename);
|
||||
return err;
|
||||
}
|
||||
|
||||
BackendError export_object(LLVMBackendCompileUnit* unit, const Target* target,
|
||||
const TargetConfig* config) {
|
||||
BackendError err = SUCCESS;
|
||||
DEBUG("exporting object file...");
|
||||
|
||||
INFO("Using target (%s): %s with features: %s", target->name.str,
|
||||
target->triple.str, target->features.str);
|
||||
|
||||
LLVMTargetRef llvm_target = NULL;
|
||||
char* error = NULL;
|
||||
|
||||
LLVMInitializeAllTargets();
|
||||
LLVMInitializeAllTargetInfos();
|
||||
LLVMInitializeAllTargetMCs();
|
||||
// NOTE: for code generation (assmebly or binary) we need the following:
|
||||
LLVMInitializeAllAsmParsers();
|
||||
LLVMInitializeAllAsmPrinters();
|
||||
|
||||
DEBUG("creating target...");
|
||||
if (LLVMGetTargetFromTriple(target->triple.str, &llvm_target, &error) !=
|
||||
0) {
|
||||
ERROR("failed to create target machine: %s", error);
|
||||
err = new_backend_impl_error(Implementation, NULL,
|
||||
"unable to create target machine");
|
||||
LLVMDisposeMessage(error);
|
||||
return err;
|
||||
}
|
||||
|
||||
DEBUG("Creating target machine...");
|
||||
LLVMTargetMachineRef target_machine = LLVMCreateTargetMachine(
|
||||
llvm_target, target->triple.str, target->cpu.str, target->features.str,
|
||||
target->opt, target->reloc, target->model);
|
||||
|
||||
print_message(Info, "Generating code for: %s", target->triple.str);
|
||||
|
||||
if (config->print_asm) {
|
||||
err = emit_module_to_file(unit, target_machine, LLVMAssemblyFile, error,
|
||||
config);
|
||||
}
|
||||
|
||||
if (err.kind != Success) {
|
||||
return err;
|
||||
}
|
||||
|
||||
err = emit_module_to_file(unit, target_machine, LLVMObjectFile, error,
|
||||
config);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void list_available_targets() {
|
||||
DEBUG("initializing all available targets...");
|
||||
LLVMInitializeAllTargetInfos();
|
||||
|
||||
printf("Available targets:\n");
|
||||
|
||||
LLVMTargetRef target = LLVMGetFirstTarget();
|
||||
while (target) {
|
||||
const char* name = LLVMGetTargetName(target);
|
||||
const char* desc = LLVMGetTargetDescription(target);
|
||||
printf(" - %s: (%s)\n", name, desc);
|
||||
|
||||
target = LLVMGetNextTarget(target);
|
||||
}
|
||||
|
||||
char* default_triple = LLVMGetDefaultTargetTriple();
|
||||
|
||||
printf("Default: %s\n", default_triple);
|
||||
|
||||
LLVMDisposeMessage(default_triple);
|
||||
}
|
||||
|
||||
BackendError export_module(LLVMBackendCompileUnit* unit, const Target* target,
|
||||
const TargetConfig* config) {
|
||||
DEBUG("exporting module...");
|
||||
|
||||
BackendError err = SUCCESS;
|
||||
|
||||
export_object(unit, target, config);
|
||||
|
||||
if (config->print_ir) {
|
||||
export_IR(unit, target, config);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static BackendError build_module(LLVMBackendCompileUnit* unit,
|
||||
LLVMGlobalScope* global_scope,
|
||||
const Module* module) {
|
||||
DEBUG("building module...");
|
||||
BackendError err = SUCCESS;
|
||||
|
||||
err = impl_types(unit, global_scope, module->types);
|
||||
if (err.kind != Success) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// NOTE: functions of boxes are not stored in the box itself,
|
||||
// thus for a box we only implement the type
|
||||
err = impl_types(unit, global_scope, module->boxes);
|
||||
if (err.kind != Success) {
|
||||
return err;
|
||||
}
|
||||
|
||||
err = impl_global_variables(unit, global_scope, module->variables);
|
||||
if (err.kind != Success) {
|
||||
return err;
|
||||
}
|
||||
|
||||
err = impl_function_types(unit, global_scope, module->functions);
|
||||
if (err.kind != Success) {
|
||||
return err;
|
||||
}
|
||||
|
||||
err = impl_functions(unit, global_scope, module->functions);
|
||||
if (err.kind != Success) {
|
||||
return err;
|
||||
}
|
||||
|
||||
char* error = NULL;
|
||||
if (LLVMVerifyModule(unit->module, LLVMReturnStatusAction, &error)) {
|
||||
print_message(Error, "Unable to compile due to: %s", error);
|
||||
LLVMDisposeMessage(error);
|
||||
err = new_backend_impl_error(Implementation, NULL, "LLVM backend verification error, see stdout");
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
BackendError parse_module(const Module* module, const TargetConfig* config) {
|
||||
DEBUG("generating code for module %p", module);
|
||||
if (module == NULL) {
|
||||
ERROR("no module for codegen");
|
||||
return new_backend_impl_error(Implementation, NULL, "no module");
|
||||
}
|
||||
|
||||
LLVMBackendCompileUnit* unit = malloc(sizeof(LLVMBackendCompileUnit));
|
||||
|
||||
// we start with a LLVM module
|
||||
DEBUG("creating LLVM context and module");
|
||||
unit->context = LLVMContextCreate();
|
||||
unit->module =
|
||||
LLVMModuleCreateWithNameInContext(config->root_module, unit->context);
|
||||
|
||||
LLVMGlobalScope* global_scope = new_global_scope(module);
|
||||
|
||||
DEBUG("generating code...");
|
||||
|
||||
BackendError err = build_module(unit, global_scope, module);
|
||||
if (err.kind == Success) {
|
||||
INFO("Module build successfully...");
|
||||
Target target = create_target_from_config(config);
|
||||
|
||||
err = export_module(unit, &target, config);
|
||||
if (err.kind == Success) {
|
||||
if (config->mode == Application) {
|
||||
TargetLinkConfig* link_config = lld_create_link_config(&target, config, module);
|
||||
|
||||
lld_link_target(link_config);
|
||||
|
||||
lld_delete_link_config(link_config);
|
||||
}
|
||||
}
|
||||
|
||||
delete_target(target);
|
||||
}
|
||||
|
||||
delete_global_scope(global_scope);
|
||||
|
||||
LLVMDisposeModule(unit->module);
|
||||
LLVMContextDispose(unit->context);
|
||||
|
||||
free(unit);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
LLVMGlobalScope* new_global_scope(const Module* module) {
|
||||
DEBUG("creating global scope...");
|
||||
LLVMGlobalScope* scope = malloc(sizeof(LLVMGlobalScope));
|
||||
|
||||
scope->module = (Module*) module;
|
||||
scope->functions = g_hash_table_new(g_str_hash, g_str_equal);
|
||||
scope->variables = g_hash_table_new(g_str_hash, g_str_equal);
|
||||
scope->types = g_hash_table_new(g_str_hash, g_str_equal);
|
||||
|
||||
return scope;
|
||||
}
|
||||
|
||||
void delete_global_scope(LLVMGlobalScope* scope) {
|
||||
DEBUG("deleting global scope...");
|
||||
g_hash_table_unref(scope->functions);
|
||||
g_hash_table_unref(scope->types);
|
||||
g_hash_table_unref(scope->variables);
|
||||
free(scope);
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
|
||||
#ifndef LLVM_BACKEND_PARSE_H_
|
||||
#define LLVM_BACKEND_PARSE_H_
|
||||
|
||||
#include <set/types.h>
|
||||
#include <codegen/backend.h>
|
||||
#include <llvm-c/Types.h>
|
||||
#include <llvm-c/Core.h>
|
||||
|
||||
typedef struct LLVMBackendCompileUnit_t {
|
||||
LLVMContextRef context;
|
||||
LLVMModuleRef module;
|
||||
} LLVMBackendCompileUnit;
|
||||
|
||||
typedef struct LLVMGlobalScope_t {
|
||||
GHashTable* types;
|
||||
// of type LLVMValueRef
|
||||
GHashTable* variables;
|
||||
// of type LLVMTypeRef
|
||||
GHashTable* functions;
|
||||
// module definition
|
||||
Module* module;
|
||||
} LLVMGlobalScope;
|
||||
|
||||
LLVMGlobalScope* new_global_scope(const Module* module);
|
||||
|
||||
void list_available_targets();
|
||||
|
||||
void delete_global_scope(LLVMGlobalScope* scope);
|
||||
|
||||
BackendError parse_module(const Module* module, const TargetConfig* config);
|
||||
|
||||
#endif // LLVM_BACKEND_PARSE_H_
|
|
@ -5,6 +5,7 @@
|
|||
#include <lex/util.h>
|
||||
#include <cfg/opt.h>
|
||||
#include <compiler.h>
|
||||
#include <llvm/parser.h>
|
||||
#include <mem/cache.h>
|
||||
|
||||
/**
|
||||
|
@ -58,9 +59,14 @@ int main(int argc, char *argv[]) {
|
|||
exit(0);
|
||||
}
|
||||
|
||||
if (is_option_set("list-targets")) {
|
||||
list_available_targets();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
run_compiler();
|
||||
|
||||
if (is_option_set("print-memory-stats")) {
|
||||
if (is_option_set("print-gc-stats")) {
|
||||
print_memory_statistics();
|
||||
}
|
||||
|
||||
|
|
104
src/mem/cache.c
104
src/mem/cache.c
|
@ -7,6 +7,7 @@
|
|||
#include <glib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <cfg/opt.h>
|
||||
|
||||
static GHashTable* namespaces = NULL;
|
||||
|
||||
|
@ -20,6 +21,17 @@ typedef struct MemoryNamespaceStatistic_t {
|
|||
size_t purged_free_count;
|
||||
} MemoryNamespaceStatistic;
|
||||
|
||||
typedef enum MemoryBlockType_t {
|
||||
GenericBlock,
|
||||
GLIB_Array,
|
||||
GLIB_HashTable
|
||||
} MemoryBlockType;
|
||||
|
||||
typedef struct MemoryBlock_t {
|
||||
void* block_ptr;
|
||||
MemoryBlockType kind;
|
||||
} MemoryBlock;
|
||||
|
||||
typedef struct MemoryNamespace_t {
|
||||
MemoryNamespaceStatistic statistic;
|
||||
GArray* blocks;
|
||||
|
@ -44,9 +56,11 @@ static void* namespace_malloc(MemoryNamespaceRef memoryNamespace, size_t size) {
|
|||
assert(memoryNamespace != NULL);
|
||||
assert(size != 0);
|
||||
|
||||
void* block = malloc(size);
|
||||
MemoryBlock block;
|
||||
block.block_ptr = malloc(size);
|
||||
block.kind = GenericBlock;
|
||||
|
||||
if (block == NULL) {
|
||||
if (block.block_ptr == NULL) {
|
||||
memoryNamespace->statistic.faulty_allocations ++;
|
||||
} else {
|
||||
g_array_append_val(memoryNamespace->blocks, block);
|
||||
|
@ -55,20 +69,36 @@ static void* namespace_malloc(MemoryNamespaceRef memoryNamespace, size_t size) {
|
|||
memoryNamespace->statistic.bytes_allocated += size;
|
||||
}
|
||||
|
||||
return block;
|
||||
return block.block_ptr;
|
||||
}
|
||||
|
||||
static void namespace_free_block(MemoryBlock block) {
|
||||
switch (block.kind) {
|
||||
case GenericBlock:
|
||||
free(block.block_ptr);
|
||||
break;
|
||||
case GLIB_Array:
|
||||
g_array_free(block.block_ptr, TRUE);
|
||||
break;
|
||||
case GLIB_HashTable:
|
||||
g_hash_table_destroy(block.block_ptr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean namespace_free(MemoryNamespaceRef memoryNamespace, void* block) {
|
||||
for (guint i = 0; i < memoryNamespace->blocks->len; i++) {
|
||||
void* current_block = g_array_index(memoryNamespace->blocks, void*, i);
|
||||
MemoryBlock current_block = g_array_index(memoryNamespace->blocks, MemoryBlock, i);
|
||||
|
||||
if (current_block == block) {
|
||||
if (current_block.block_ptr == block) {
|
||||
assert(block != NULL);
|
||||
|
||||
free(block);
|
||||
namespace_free_block(current_block);
|
||||
|
||||
g_array_remove_index(memoryNamespace->blocks, i);
|
||||
|
||||
memoryNamespace->statistic.manual_free_count++;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
@ -79,13 +109,13 @@ static void* namespace_realloc(MemoryNamespaceRef memoryNamespace, void* block,
|
|||
void* reallocated_block = NULL;
|
||||
|
||||
for (guint i = 0; i < memoryNamespace->blocks->len; i++) {
|
||||
void* current_block = g_array_index(memoryNamespace->blocks, void*, i);
|
||||
MemoryBlock current_block = g_array_index(memoryNamespace->blocks, MemoryBlock, i);
|
||||
|
||||
if (current_block == block) {
|
||||
reallocated_block = realloc(block, size);
|
||||
if (current_block.block_ptr == block) {
|
||||
reallocated_block = realloc(current_block.block_ptr, size);
|
||||
|
||||
if (reallocated_block != NULL) {
|
||||
g_array_index(memoryNamespace->blocks, void*, i) = reallocated_block;
|
||||
g_array_index(memoryNamespace->blocks, MemoryBlock, i).block_ptr = reallocated_block;
|
||||
memoryNamespace->statistic.bytes_allocated += size;
|
||||
memoryNamespace->statistic.reallocation_count ++;
|
||||
} else {
|
||||
|
@ -107,9 +137,9 @@ static void namespace_delete(MemoryNamespaceRef memoryNamespace) {
|
|||
static void namespace_purge(MemoryNamespaceRef memoryNamespace) {
|
||||
|
||||
for (guint i = 0; i < memoryNamespace->blocks->len; i++) {
|
||||
void* current_block = g_array_index(memoryNamespace->blocks, void*, i);
|
||||
MemoryBlock current_block = g_array_index(memoryNamespace->blocks, MemoryBlock, i);
|
||||
|
||||
free(current_block);
|
||||
namespace_free_block(current_block);
|
||||
|
||||
memoryNamespace->statistic.purged_free_count ++;
|
||||
}
|
||||
|
@ -120,7 +150,7 @@ static void namespace_purge(MemoryNamespaceRef memoryNamespace) {
|
|||
static MemoryNamespaceRef namespace_new() {
|
||||
MemoryNamespaceRef memoryNamespace = malloc(sizeof(MemoryNamespace));
|
||||
|
||||
memoryNamespace->blocks = g_array_new(FALSE, FALSE, sizeof(void*));
|
||||
memoryNamespace->blocks = g_array_new(FALSE, FALSE, sizeof(MemoryBlock));
|
||||
memoryNamespace->statistic.bytes_allocated = 0;
|
||||
memoryNamespace->statistic.allocation_count = 0;
|
||||
memoryNamespace->statistic.manual_free_count = 0;
|
||||
|
@ -132,6 +162,30 @@ static MemoryNamespaceRef namespace_new() {
|
|||
return memoryNamespace;
|
||||
}
|
||||
|
||||
GArray *namespace_new_g_array(MemoryNamespaceRef namespace, guint size) {
|
||||
MemoryBlock block;
|
||||
block.block_ptr = g_array_new(FALSE, FALSE, size);
|
||||
block.kind = GLIB_Array;
|
||||
|
||||
g_array_append_val(namespace->blocks, block);
|
||||
namespace->statistic.bytes_allocated += sizeof(GArray*);
|
||||
namespace->statistic.allocation_count ++;
|
||||
|
||||
return block.block_ptr;
|
||||
}
|
||||
|
||||
GHashTable *namespace_new_g_hash_table(MemoryNamespaceRef namespace, GHashFunc hash_func, GEqualFunc key_equal_func) {
|
||||
MemoryBlock block;
|
||||
block.block_ptr = g_hash_table_new(hash_func, key_equal_func);
|
||||
block.kind = GLIB_HashTable;
|
||||
|
||||
g_array_append_val(namespace->blocks, block);
|
||||
namespace->statistic.bytes_allocated += sizeof(GHashTable*);
|
||||
namespace->statistic.allocation_count ++;
|
||||
|
||||
return block.block_ptr;
|
||||
}
|
||||
|
||||
static void cleanup() {
|
||||
if (namespaces == NULL) {
|
||||
printf("==> Memory cache was unused <==\n");
|
||||
|
@ -221,7 +275,7 @@ void mem_purge_namespace(MemoryNamespaceName name) {
|
|||
|
||||
namespace_purge(cache);
|
||||
} else {
|
||||
PANIC("purging invalid namespace: %s", name);
|
||||
WARN("purging invalid namespace: %s", name);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -267,5 +321,25 @@ void print_memory_statistics() {
|
|||
|
||||
namespace_statistics_print(&total, "summary");
|
||||
|
||||
printf("Note: untracked are memory allocations from external libraries.\n");
|
||||
printf("Note: untracked are memory allocations from external libraries and non-gc managed components.\n");
|
||||
}
|
||||
|
||||
GArray* mem_new_g_array(MemoryNamespaceName name, guint element_size) {
|
||||
MemoryNamespaceRef cache = check_namespace(name);
|
||||
|
||||
if (cache == NULL) {
|
||||
PANIC("memory namespace not created");
|
||||
}
|
||||
|
||||
return namespace_new_g_array(cache, element_size);
|
||||
}
|
||||
|
||||
GHashTable* mem_new_g_hash_table(MemoryNamespaceName name, GHashFunc hash_func, GEqualFunc key_equal_func) {
|
||||
MemoryNamespaceRef cache = check_namespace(name);
|
||||
|
||||
if (cache == NULL) {
|
||||
PANIC("memory namespace not created");
|
||||
}
|
||||
|
||||
return namespace_new_g_hash_table(cache, hash_func, key_equal_func);
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
#include <mem/cache.h>
|
||||
#include <stddef.h>
|
||||
#include <glib.h>
|
||||
|
||||
typedef char* MemoryNamespaceName;
|
||||
|
||||
|
@ -16,6 +17,7 @@ typedef char* MemoryNamespaceName;
|
|||
#define MemoryNamespaceOpt "Options"
|
||||
#define MemoryNamespaceSet "SET"
|
||||
#define MemoryNamespaceLlvm "LLVM"
|
||||
#define MemoryNamespaceLld "LLD"
|
||||
#define MemoryNamespaceIo "I/O"
|
||||
#define MemoryNamespaceStatic "Static"
|
||||
|
||||
|
@ -86,4 +88,8 @@ void* mem_clone(MemoryNamespaceName name, void* data, size_t size);
|
|||
|
||||
void print_memory_statistics();
|
||||
|
||||
GArray* mem_new_g_array(MemoryNamespaceName name, guint element_size);
|
||||
|
||||
GHashTable* mem_new_g_hash_table(MemoryNamespaceName name, GHashFunc hash_func, GEqualFunc key_equal_func);
|
||||
|
||||
#endif //GEMSTONE_CACHE_H
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,19 @@
|
|||
#ifndef _SET_H_
|
||||
#define _SET_H_
|
||||
|
||||
#include <ast/ast.h>
|
||||
#include <set/types.h>
|
||||
|
||||
#define SEMANTIC_OK 0
|
||||
#define SEMANTIC_ERROR 1
|
||||
|
||||
// type of string literal
|
||||
extern const Type StringLiteralType;
|
||||
|
||||
Module * create_set(AST_NODE_PTR rootNodePtr );
|
||||
|
||||
void delete_set(Module* module);
|
||||
|
||||
bool compareTypes(Type *leftType, Type *rightType);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,620 @@
|
|||
|
||||
#ifndef SET_TYPES_H_
|
||||
#define SET_TYPES_H_
|
||||
|
||||
#include <glib.h>
|
||||
#include <ast/ast.h>
|
||||
|
||||
// with of primitive types (int/float) in bytes
|
||||
#define BASE_BYTES 4
|
||||
|
||||
/**
|
||||
* @brief Primitive types form the basis of all other types.
|
||||
*
|
||||
*/
|
||||
typedef enum PrimitiveType_t {
|
||||
// 4 byte signed integer in two's complement
|
||||
Int =0,
|
||||
// 4 byte IEEE-754 single precision
|
||||
Float =1
|
||||
} PrimitiveType;
|
||||
|
||||
/**
|
||||
* @brief Represents the sign of a composite type.
|
||||
*
|
||||
*/
|
||||
typedef enum Sign_t {
|
||||
// type has no sign bit
|
||||
Unsigned = 0,
|
||||
// type has a sign bit
|
||||
Signed = 1
|
||||
} Sign;
|
||||
|
||||
/**
|
||||
* @brief Represents the scale of composite type which is multiplied
|
||||
* with the base size in order to retrieve the the composites size.
|
||||
* @attention Valid value are: { 1/8, 1/4, 1/2, 1, 2, 4, 8 }
|
||||
*
|
||||
*/
|
||||
typedef double Scale;
|
||||
|
||||
/**
|
||||
* @brief A composite type is an extended definition of a primitive type.
|
||||
*
|
||||
*/
|
||||
typedef struct CompositeType_t {
|
||||
// sign of composite
|
||||
Sign sign;
|
||||
Scale scale;
|
||||
PrimitiveType primitive;
|
||||
AST_NODE_PTR nodePtr;
|
||||
} CompositeType;
|
||||
|
||||
/**
|
||||
* @brief Specifies the specific type of the generic type struct.
|
||||
*
|
||||
*/
|
||||
typedef enum TypeKind_t {
|
||||
TypeKindPrimitive,
|
||||
TypeKindComposite,
|
||||
TypeKindBox,
|
||||
TypeKindReference
|
||||
} TypeKind;
|
||||
|
||||
typedef struct Type_t Type;
|
||||
|
||||
/**
|
||||
* @brief Reference points to a type.
|
||||
* @attention Can be nested. A reference can point to another reference: REF -> REF -> REF -> Primitive
|
||||
*
|
||||
*/
|
||||
typedef Type* ReferenceType;
|
||||
|
||||
typedef struct BoxType_t BoxType;
|
||||
|
||||
typedef struct Block_t Block;
|
||||
|
||||
typedef struct Expression_t Expression;
|
||||
|
||||
typedef struct BoxMember_t {
|
||||
const char* name;
|
||||
Type* type;
|
||||
BoxType* box;
|
||||
Expression* initalizer;
|
||||
AST_NODE_PTR nodePtr;
|
||||
} BoxMember;
|
||||
|
||||
/**
|
||||
* @brief Essentially a g lorified struct
|
||||
*
|
||||
*/
|
||||
typedef struct BoxType_t {
|
||||
// hashtable of members.
|
||||
// Associates the memebers name (const char*) with its type (BoxMember)
|
||||
GHashTable* member; //BoxMember Pointer
|
||||
AST_NODE_PTR nodePtr;
|
||||
} BoxType;
|
||||
|
||||
typedef struct Variable_t Variable;
|
||||
|
||||
typedef struct BoxAccess_t {
|
||||
// list of recursive box accesses
|
||||
// contains a list of BoxMembers (each specifying their own type, name and box type)
|
||||
GArray* member;
|
||||
// box variable to access
|
||||
Variable* variable;
|
||||
AST_NODE_PTR nodePtr;
|
||||
} BoxAccess;
|
||||
|
||||
typedef struct Type_t {
|
||||
// specifiy the kind of this type
|
||||
// used to determine which implementation to choose
|
||||
TypeKind kind;
|
||||
// actual implementation of the type
|
||||
union TypeImplementation_t {
|
||||
PrimitiveType primitive;
|
||||
CompositeType composite;
|
||||
BoxType* box;
|
||||
ReferenceType reference;
|
||||
} impl;
|
||||
AST_NODE_PTR nodePtr;
|
||||
} Type;
|
||||
|
||||
typedef struct Typedefine_t {
|
||||
const char* name;
|
||||
Type *type;
|
||||
AST_NODE_PTR nodePtr;
|
||||
} Typedefine;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Reprents the value of type. Can be used to definitions, initialization and for expressions contants.
|
||||
*
|
||||
*/
|
||||
typedef struct TypeValue_t {
|
||||
// the type
|
||||
Type *type;
|
||||
// UTF-8 representation of the type's value
|
||||
const char* value;
|
||||
AST_NODE_PTR nodePtr;
|
||||
} TypeValue;
|
||||
|
||||
// .------------------------------------------------.
|
||||
// | Functions |
|
||||
// '------------------------------------------------'
|
||||
|
||||
/**
|
||||
* @brief Specifies a parameters I/O properties
|
||||
*
|
||||
*/
|
||||
typedef enum IO_Qualifier_t {
|
||||
// Can be read from but not written to.
|
||||
// Function local only.
|
||||
In,
|
||||
// Can be written to but not read from.
|
||||
// Passed back to the functions callee.
|
||||
Out,
|
||||
// Can be read from and written to.
|
||||
// Passed back to the functions callee.
|
||||
InOut,
|
||||
} IO_Qualifier;
|
||||
|
||||
/**
|
||||
* @brief A functions parameter declaration.
|
||||
*
|
||||
*/
|
||||
typedef struct ParameterDeclaration_t {
|
||||
Type *type;
|
||||
IO_Qualifier qualifier;
|
||||
AST_NODE_PTR nodePtr;
|
||||
} ParameterDeclaration;
|
||||
|
||||
/**
|
||||
* @brief A functions parameter.
|
||||
*
|
||||
*/
|
||||
typedef struct ParameterDefinition_t {
|
||||
ParameterDeclaration declaration;
|
||||
// value to initalize the declaration with
|
||||
// NOTE: type of initializer and declaration MUST be equal
|
||||
Expression *initializer;
|
||||
AST_NODE_PTR nodePtr;
|
||||
} ParameterDefinition;
|
||||
|
||||
typedef enum ParameterKind_t {
|
||||
ParameterDeclarationKind,
|
||||
ParameterDefinitionKind
|
||||
} ParameterKind;
|
||||
|
||||
/**
|
||||
* @brief A parameter can either be a declaration or a definition
|
||||
*
|
||||
*/
|
||||
typedef struct Parameter_t {
|
||||
const char* name;
|
||||
|
||||
ParameterKind kind;
|
||||
union ParameterImplementation {
|
||||
ParameterDeclaration declaration;
|
||||
ParameterDefinition definiton;
|
||||
} impl;
|
||||
AST_NODE_PTR nodePtr;
|
||||
} Parameter; // fix typo
|
||||
|
||||
typedef enum FunctionKind_t {
|
||||
FunctionDeclarationKind,
|
||||
FunctionDefinitionKind
|
||||
} FunctionKind;
|
||||
|
||||
typedef struct FunctionDefinition_t {
|
||||
// hashtable of parameters
|
||||
// associates a parameters name (const char*) with its parameter declaration (ParameterDeclaration)
|
||||
GArray* parameter; // Parameter
|
||||
AST_NODE_PTR nodePtr;
|
||||
// body of function
|
||||
Block *body;
|
||||
// name of function
|
||||
const char* name;
|
||||
} FunctionDefinition;
|
||||
|
||||
typedef struct FunctionDeclaration_t {
|
||||
// hashtable of parameters
|
||||
// associates a parameters name (const char*) with its parameter declaration (ParameterDeclaration)
|
||||
GArray* parameter; // Parameter
|
||||
AST_NODE_PTR nodePtr;
|
||||
const char* name;
|
||||
} FunctionDeclaration;
|
||||
|
||||
typedef struct Function_t {
|
||||
FunctionKind kind;
|
||||
union FunctionImplementation {
|
||||
FunctionDefinition definition;
|
||||
FunctionDeclaration declaration;
|
||||
} impl;
|
||||
AST_NODE_PTR nodePtr;
|
||||
const char * name;
|
||||
} Function;
|
||||
|
||||
// .------------------------------------------------.
|
||||
// | Variables |
|
||||
// '------------------------------------------------'
|
||||
|
||||
typedef enum StorageQualifier_t {
|
||||
Local,
|
||||
Static,
|
||||
Global
|
||||
} StorageQualifier;
|
||||
|
||||
typedef struct VariableDeclaration_t {
|
||||
StorageQualifier qualifier;
|
||||
Type *type;
|
||||
AST_NODE_PTR nodePtr;
|
||||
} VariableDeclaration;
|
||||
|
||||
/**
|
||||
* @brief Definition of a variable
|
||||
*
|
||||
* @attention NOTE: The types of the initializer and the declaration must be equal
|
||||
*
|
||||
*/
|
||||
typedef struct VariableDefiniton_t {
|
||||
VariableDeclaration declaration;
|
||||
Expression *initializer;
|
||||
AST_NODE_PTR nodePtr;
|
||||
} VariableDefiniton;
|
||||
|
||||
typedef enum VariableKind_t {
|
||||
VariableKindDeclaration,
|
||||
VariableKindDefinition,
|
||||
VariableKindBoxMember
|
||||
} VariableKind;
|
||||
|
||||
typedef struct Variable_t {
|
||||
VariableKind kind;
|
||||
const char* name;
|
||||
union VariableImplementation {
|
||||
VariableDeclaration declaration;
|
||||
VariableDefiniton definiton;
|
||||
BoxAccess member;
|
||||
} impl;
|
||||
AST_NODE_PTR nodePtr;
|
||||
} Variable;
|
||||
|
||||
typedef struct Dereference_t {
|
||||
Expression* index;
|
||||
Expression* variable;
|
||||
AST_NODE_PTR nodePtr;
|
||||
}Dereference;
|
||||
|
||||
typedef struct StorageExpr_t StorageExpr;
|
||||
|
||||
typedef struct StorageDereference_t {
|
||||
Expression* index;
|
||||
StorageExpr* array;
|
||||
AST_NODE_PTR nodePtr;
|
||||
} StorageDereference;
|
||||
|
||||
typedef struct AddressOf_t {
|
||||
Expression* variable;
|
||||
AST_NODE_PTR node_ptr;
|
||||
}AddressOf;
|
||||
|
||||
// .------------------------------------------------.
|
||||
// | Casts |
|
||||
// '------------------------------------------------'
|
||||
|
||||
/**
|
||||
* @brief Perform a type cast, converting a value to different type whilest preserving as much of the original
|
||||
* values information.
|
||||
*
|
||||
* @attention NOTE: Must check wether the given value's type can be parsed into
|
||||
* the target type without loss.
|
||||
* Lossy mean possibly loosing information such when casting a float into an int (no fraction anymore).
|
||||
*
|
||||
*/
|
||||
typedef struct TypeCast_t {
|
||||
Type *targetType;
|
||||
Expression* operand;
|
||||
AST_NODE_PTR nodePtr;
|
||||
} TypeCast;
|
||||
|
||||
/**
|
||||
* @brief Perform a reinterpret cast.
|
||||
*
|
||||
* @attention NOTE: The given value's type must have the size in bytes as the target type.
|
||||
* Transmuting a short int into a float should yield an error.
|
||||
*
|
||||
*/
|
||||
typedef struct Transmute_t {
|
||||
Type *targetType;
|
||||
Expression* operand;
|
||||
AST_NODE_PTR nodePtr;
|
||||
} Transmute;
|
||||
|
||||
// .------------------------------------------------.
|
||||
// | Arithmetic |
|
||||
// '------------------------------------------------'
|
||||
|
||||
/**
|
||||
* @brief Represents the arithmetic operator.
|
||||
*
|
||||
*/
|
||||
typedef enum ArithmeticOperator_t {
|
||||
Add,
|
||||
Sub,
|
||||
Mul,
|
||||
Div,
|
||||
Negate
|
||||
} ArithmeticOperator;
|
||||
|
||||
// .------------------------------------------------.
|
||||
// | Relational |
|
||||
// '------------------------------------------------'
|
||||
|
||||
/**
|
||||
* @brief Represents the relational operator.
|
||||
*
|
||||
*/
|
||||
typedef enum RelationalOperator_t {
|
||||
Equal,
|
||||
Greater,
|
||||
Less
|
||||
} RelationalOperator;
|
||||
|
||||
// .------------------------------------------------.
|
||||
// | Boolean |
|
||||
// '------------------------------------------------'
|
||||
|
||||
typedef enum BooleanOperator_t {
|
||||
BooleanAnd,
|
||||
BooleanOr,
|
||||
BooleanNot,
|
||||
BooleanXor,
|
||||
} BooleanOperator;
|
||||
|
||||
// .------------------------------------------------.
|
||||
// | Logical |
|
||||
// '------------------------------------------------'
|
||||
|
||||
typedef enum LogicalOperator_t {
|
||||
LogicalAnd,
|
||||
LogicalOr,
|
||||
LogicalNot,
|
||||
LogicalXor,
|
||||
} LogicalOperator;
|
||||
|
||||
// .------------------------------------------------.
|
||||
// | Logical |
|
||||
// '------------------------------------------------'
|
||||
|
||||
typedef enum BitwiseOperator_t {
|
||||
BitwiseAnd,
|
||||
BitwiseOr,
|
||||
BitwiseNot,
|
||||
BitwiseXor,
|
||||
} BitwiseOperator;
|
||||
|
||||
// .------------------------------------------------.
|
||||
// | Operations |
|
||||
// '------------------------------------------------'
|
||||
|
||||
typedef enum OperationKind_t {
|
||||
Arithmetic,
|
||||
Relational,
|
||||
Boolean,
|
||||
Logical,
|
||||
Bitwise
|
||||
} OperationKind;
|
||||
|
||||
typedef struct Operation_t {
|
||||
// mode of operation
|
||||
OperationKind kind;
|
||||
// specific implementation
|
||||
union OperationImplementation {
|
||||
ArithmeticOperator arithmetic;
|
||||
RelationalOperator relational;
|
||||
BooleanOperator boolean;
|
||||
LogicalOperator logical;
|
||||
BitwiseOperator bitwise;
|
||||
} impl;
|
||||
GArray* operands; //Expression*
|
||||
AST_NODE_PTR nodePtr;
|
||||
} Operation;
|
||||
|
||||
// .------------------------------------------------.
|
||||
// | Expression |
|
||||
// '------------------------------------------------'
|
||||
|
||||
typedef enum ExpressionKind_t {
|
||||
ExpressionKindOperation,
|
||||
ExpressionKindTypeCast,
|
||||
ExpressionKindTransmute,
|
||||
ExpressionKindConstant,
|
||||
ExpressionKindVariable,
|
||||
ExpressionKindDereference,
|
||||
ExpressionKindAddressOf,
|
||||
} ExpressionKind;
|
||||
|
||||
typedef struct Expression_t {
|
||||
ExpressionKind kind;
|
||||
// type of resulting data
|
||||
Type* result;
|
||||
union ExpressionImplementation_t {
|
||||
Operation operation;
|
||||
TypeCast typecast;
|
||||
Transmute transmute;
|
||||
TypeValue constant;
|
||||
Variable* variable;
|
||||
Dereference dereference;
|
||||
AddressOf addressOf;
|
||||
} impl;
|
||||
AST_NODE_PTR nodePtr;
|
||||
} Expression;
|
||||
|
||||
// .------------------------------------------------.
|
||||
// | Function call |
|
||||
// '------------------------------------------------'
|
||||
|
||||
typedef struct FunctionCall_t {
|
||||
// function to call
|
||||
Function* function;
|
||||
// list of expression arguments
|
||||
GArray* expressions;
|
||||
AST_NODE_PTR nodePtr;
|
||||
} FunctionCall;
|
||||
|
||||
typedef struct FunctionBoxCall_t {
|
||||
// function to call
|
||||
Function* function;
|
||||
// list of expression arguments
|
||||
GArray* expressions;
|
||||
// box which has the function defined for it
|
||||
// NOTE: must be of TypeKind: Box
|
||||
Variable selfArgument;
|
||||
AST_NODE_PTR nodePtr;
|
||||
} FunctionBoxCall;
|
||||
|
||||
typedef struct Block_t {
|
||||
// array of statements
|
||||
GArray* statemnts; // array of type(Statement)
|
||||
AST_NODE_PTR nodePtr;
|
||||
} Block;
|
||||
|
||||
// .------------------------------------------------.
|
||||
// | While |
|
||||
// '------------------------------------------------'
|
||||
|
||||
typedef struct While_t {
|
||||
Expression *conditon;
|
||||
Block block;
|
||||
AST_NODE_PTR nodePtr;
|
||||
} While;
|
||||
|
||||
// .------------------------------------------------.
|
||||
// | If/Else |
|
||||
// '------------------------------------------------'
|
||||
|
||||
typedef struct If_t {
|
||||
Expression *conditon;
|
||||
Block block;
|
||||
AST_NODE_PTR nodePtr;
|
||||
} If;
|
||||
|
||||
typedef struct ElseIf_t {
|
||||
Expression *conditon;
|
||||
Block block;
|
||||
AST_NODE_PTR nodePtr;
|
||||
} ElseIf;
|
||||
|
||||
typedef struct Else_t {
|
||||
Block block;
|
||||
AST_NODE_PTR nodePtr;
|
||||
} Else;
|
||||
|
||||
typedef struct Branch_t {
|
||||
If ifBranch;
|
||||
// list of else-ifs (can be empty/NULL)
|
||||
GArray* elseIfBranches;
|
||||
Else elseBranch;
|
||||
AST_NODE_PTR nodePtr;
|
||||
} Branch;
|
||||
|
||||
// .------------------------------------------------.
|
||||
// | Statements |
|
||||
// '------------------------------------------------'
|
||||
|
||||
typedef enum StorageExprKind_t {
|
||||
StorageExprKindVariable,
|
||||
StorageExprKindBoxAccess,
|
||||
StorageExprKindDereference,
|
||||
} StorageExprKind;
|
||||
|
||||
typedef struct StorageExpr_t {
|
||||
StorageExprKind kind;
|
||||
Type* target_type;
|
||||
union StorageExprImpl {
|
||||
Variable* variable;
|
||||
BoxAccess boxAccess;
|
||||
StorageDereference dereference;
|
||||
} impl;
|
||||
} StorageExpr;
|
||||
|
||||
typedef struct Assignment_t {
|
||||
StorageExpr* destination;
|
||||
Expression* value;
|
||||
AST_NODE_PTR nodePtr;
|
||||
} Assignment;
|
||||
|
||||
typedef enum StatementKind_t {
|
||||
StatementKindFunctionCall,
|
||||
StatementKindFunctionBoxCall,
|
||||
StatementKindWhile,
|
||||
StatementKindBranch,
|
||||
StatementKindAssignment,
|
||||
StatementKindDeclaration,
|
||||
StatementKindDefinition
|
||||
} StatementKind;
|
||||
|
||||
typedef struct Statement_t {
|
||||
StatementKind kind;
|
||||
union StatementImplementation {
|
||||
FunctionCall call;
|
||||
FunctionBoxCall boxCall;
|
||||
While whileLoop;
|
||||
Branch branch;
|
||||
Assignment assignment;
|
||||
Variable *variable;
|
||||
} impl;
|
||||
AST_NODE_PTR nodePtr;
|
||||
} Statement;
|
||||
|
||||
// .------------------------------------------------.
|
||||
// | Module |
|
||||
// '------------------------------------------------'
|
||||
|
||||
typedef struct Module_t {
|
||||
GHashTable* boxes; //BoxType
|
||||
GHashTable* types; //
|
||||
GHashTable* functions;
|
||||
GHashTable* variables;
|
||||
// to be resolved after the module has been parsed completely
|
||||
GArray* imports;
|
||||
} Module;
|
||||
|
||||
|
||||
// .------------------------------------------------.
|
||||
// | Cleanup Code |
|
||||
// '------------------------------------------------'
|
||||
|
||||
void delete_box_access(BoxAccess* access);
|
||||
|
||||
void delete_variable(Variable* variable);
|
||||
|
||||
void delete_type(Type* type);
|
||||
|
||||
void delete_box(BoxType* box);
|
||||
|
||||
void delete_declaration(VariableDeclaration* decl);
|
||||
|
||||
void delete_definition(VariableDefiniton* definition);
|
||||
|
||||
void delete_expression(Expression* expr);
|
||||
|
||||
void delete_operation(Operation* operation);
|
||||
|
||||
void delete_type_value(TypeValue* value);
|
||||
|
||||
void delete_transmute(Transmute* trans);
|
||||
|
||||
void delete_typecast(TypeCast* cast);
|
||||
|
||||
void delete_box_member(BoxMember* member);
|
||||
|
||||
void delete_box_type(BoxType *box_type);
|
||||
|
||||
void delete_composite([[maybe_unused]] CompositeType* composite);
|
||||
|
||||
void delete_module(Module* module);
|
||||
|
||||
#endif // SET_TYPES_H_
|
|
@ -3,6 +3,7 @@
|
|||
#include <string.h>
|
||||
#include <sys/col.h>
|
||||
#include <sys/log.h>
|
||||
#include <cfg/opt.h>
|
||||
|
||||
#ifdef __unix__
|
||||
#include <unistd.h>
|
||||
|
@ -55,6 +56,10 @@ void enable_ansi_colors() {
|
|||
|
||||
int stdout_supports_ansi_esc() {
|
||||
|
||||
if (is_option_set("color-always")) {
|
||||
return ANSI_ENABLED;
|
||||
}
|
||||
|
||||
#ifdef __unix__
|
||||
// check if TTY
|
||||
if (isatty(STDOUT_FILENO)) {
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
extern int yylex();
|
||||
extern AST_NODE_PTR root;
|
||||
|
||||
#define new_loc() new_location(yylloc.first_line, yylloc.first_column, yylloc.last_line, yylloc.last_column)
|
||||
#define new_loc() new_location(yylloc.first_line, yylloc.first_column, yylloc.last_line, yylloc.last_column, current_file)
|
||||
}
|
||||
|
||||
%union {
|
||||
|
@ -55,6 +55,7 @@
|
|||
%type <node_ptr> moduleimport
|
||||
%type <node_ptr> programbody
|
||||
%type <node_ptr> fundef
|
||||
%type <node_ptr> fundecl
|
||||
%type <node_ptr> box
|
||||
%type <node_ptr> typedef
|
||||
%type <node_ptr> exprlist
|
||||
|
@ -68,6 +69,7 @@
|
|||
%type <node_ptr> typecast
|
||||
%type <node_ptr> reinterpretcast
|
||||
%type <node_ptr> program
|
||||
%type <node_ptr> storage_expr
|
||||
|
||||
|
||||
%token KeyInt
|
||||
|
@ -135,11 +137,13 @@
|
|||
%left '(' ')' '[' ']'
|
||||
|
||||
%%
|
||||
program: program programbody {AST_push_node(root, $2);}
|
||||
program: program programbody {AST_push_node(root, $2);
|
||||
}
|
||||
| programbody {AST_push_node(root, $1);};
|
||||
|
||||
programbody: moduleimport {$$ = $1;}
|
||||
| fundef{$$ = $1;}
|
||||
| fundecl{$$ = $1;}
|
||||
| box{$$ = $1;}
|
||||
| definition{$$ = $1;}
|
||||
| decl{$$ = $1;}
|
||||
|
@ -190,6 +194,13 @@ fundef: KeyFun Ident paramlist '{' statementlist'}' {AST_NODE_PTR fun = AST_new_
|
|||
$$ = fun;
|
||||
DEBUG("Function");};
|
||||
|
||||
fundecl: KeyFun Ident paramlist {AST_NODE_PTR fun = AST_new_node(new_loc(), AST_Fun, NULL);
|
||||
AST_NODE_PTR ident = AST_new_node(new_loc(), AST_Ident, $2);
|
||||
AST_push_node(fun, ident);
|
||||
AST_push_node(fun, $3);
|
||||
$$ = fun;
|
||||
DEBUG("Function");};
|
||||
|
||||
paramlist: paramlist '(' params ')' {AST_push_node($1, $3);
|
||||
$$ = $1;}
|
||||
| paramlist '(' ')'{$$ = $1;}
|
||||
|
@ -376,7 +387,7 @@ decl: type ':' identlist {AST_NODE_PTR decl = AST_new_node(new_loc(), AST_Decl,
|
|||
AST_push_node(decl, $1);
|
||||
AST_push_node(decl, $2);
|
||||
AST_push_node(decl, $4);
|
||||
$$ = decl;}
|
||||
$$ = decl;};
|
||||
|
||||
|
||||
definition: decl '=' expr { AST_NODE_PTR def = AST_new_node(new_loc(), AST_Def, NULL);
|
||||
|
@ -389,15 +400,18 @@ storagequalifier: KeyGlobal {$$ = AST_new_node(new_loc(), AST_Storage, "global")
|
|||
| KeyStatic {$$ = AST_new_node(new_loc(), AST_Storage, "static");}
|
||||
| KeyLocal {$$ = AST_new_node(new_loc(), AST_Storage, "local");};
|
||||
|
||||
assign: Ident '=' expr { AST_NODE_PTR assign = AST_new_node(new_loc(), AST_Assign, NULL);
|
||||
AST_NODE_PTR ident = AST_new_node(new_loc(), AST_Ident, $1);
|
||||
AST_push_node(assign, ident);
|
||||
assign: storage_expr '=' expr { AST_NODE_PTR assign = AST_new_node(new_loc(), AST_Assign, NULL);
|
||||
AST_push_node(assign, $1);
|
||||
AST_push_node(assign, $3);
|
||||
$$ = assign;
|
||||
DEBUG("Assignment"); }
|
||||
$$ = assign; };
|
||||
|
||||
| boxaccess '=' expr
|
||||
| boxselfaccess '=' expr ;
|
||||
storage_expr: Ident { $$ = AST_new_node(new_loc(), AST_Ident, $1); }
|
||||
| boxaccess { $$ = $1; }
|
||||
| boxselfaccess { $$ = $1; }
|
||||
| storage_expr '[' expr ']' { AST_NODE_PTR deref = AST_new_node(new_loc(), AST_Dereference, NULL);
|
||||
AST_push_node(deref, $1);
|
||||
AST_push_node(deref, $3);
|
||||
$$ = deref; };
|
||||
|
||||
sign: KeySigned {$$ = AST_new_node(new_loc(), AST_Sign, "signed");}
|
||||
| KeyUnsigned{$$ = AST_new_node(new_loc(), AST_Sign, "unsigned");};
|
||||
|
@ -534,6 +548,6 @@ opbit: expr OpBitand expr {AST_NODE_PTR and = AST_new_node(new_loc(), AST_BitAnd
|
|||
|
||||
int yyerror(const char *s) {
|
||||
TokenLocation location = new_loc();
|
||||
print_diagnostic(current_file, &location, Error, s);
|
||||
print_diagnostic(&location, Error, s);
|
||||
return 0;
|
||||
}
|
|
@ -10,5 +10,6 @@ add_subdirectory(logging)
|
|||
add_subdirectory(input_file)
|
||||
add_subdirectory(ast)
|
||||
add_subdirectory(glib)
|
||||
add_subdirectory(llvm)
|
||||
add_subdirectory(project)
|
||||
add_subdirectory(cache)
|
||||
|
|
|
@ -7,22 +7,22 @@
|
|||
#include <mem/cache.h>
|
||||
|
||||
void generate_statement(const AST_NODE_PTR stmt) {
|
||||
const AST_NODE_PTR add = AST_new_node(empty_location(), AST_Add, NULL);
|
||||
const AST_NODE_PTR add = AST_new_node(empty_location(NULL), AST_Add, NULL);
|
||||
|
||||
AST_push_node(add, AST_new_node(empty_location(), AST_Int, "3"));
|
||||
AST_push_node(add, AST_new_node(empty_location(), AST_Int, "6"));
|
||||
AST_push_node(add, AST_new_node(empty_location(NULL), AST_Int, "3"));
|
||||
AST_push_node(add, AST_new_node(empty_location(NULL), AST_Int, "6"));
|
||||
|
||||
AST_push_node(stmt, add);
|
||||
}
|
||||
|
||||
void generate_branch(const AST_NODE_PTR stmt) {
|
||||
const AST_NODE_PTR branch = AST_new_node(empty_location(), AST_If, NULL);
|
||||
const AST_NODE_PTR gt = AST_new_node(empty_location(), AST_Greater, NULL);
|
||||
const AST_NODE_PTR branch = AST_new_node(empty_location(NULL), AST_If, NULL);
|
||||
const AST_NODE_PTR gt = AST_new_node(empty_location(NULL), AST_Greater, NULL);
|
||||
|
||||
AST_push_node(branch, gt);
|
||||
|
||||
AST_push_node(gt, AST_new_node(empty_location(), AST_Float, "2.3"));
|
||||
AST_push_node(gt, AST_new_node(empty_location(), AST_Float, "0.79"));
|
||||
AST_push_node(gt, AST_new_node(empty_location(NULL), AST_Float, "2.3"));
|
||||
AST_push_node(gt, AST_new_node(empty_location(NULL), AST_Float, "0.79"));
|
||||
|
||||
AST_push_node(stmt, branch);
|
||||
|
||||
|
@ -32,7 +32,7 @@ void generate_branch(const AST_NODE_PTR stmt) {
|
|||
int main(void) {
|
||||
mem_init();
|
||||
|
||||
const AST_NODE_PTR root = AST_new_node(empty_location(), AST_Stmt, NULL);
|
||||
const AST_NODE_PTR root = AST_new_node(empty_location(NULL), AST_Stmt, NULL);
|
||||
|
||||
generate_branch(root);
|
||||
|
||||
|
|
|
@ -9,15 +9,15 @@
|
|||
int main(void) {
|
||||
mem_init();
|
||||
|
||||
struct AST_Node_t* node = AST_new_node(empty_location(), AST_If, NULL);
|
||||
struct AST_Node_t* node = AST_new_node(empty_location(NULL), AST_If, NULL);
|
||||
|
||||
struct AST_Node_t* child = AST_new_node(empty_location(), AST_Add, NULL);
|
||||
AST_push_node(child, AST_new_node(empty_location(), AST_Int, "43"));
|
||||
AST_push_node(child, AST_new_node(empty_location(), AST_Int, "9"));
|
||||
struct AST_Node_t* child = AST_new_node(empty_location(NULL), AST_Add, NULL);
|
||||
AST_push_node(child, AST_new_node(empty_location(NULL), AST_Int, "43"));
|
||||
AST_push_node(child, AST_new_node(empty_location(NULL), AST_Int, "9"));
|
||||
|
||||
AST_push_node(node, child);
|
||||
AST_push_node(node, AST_new_node(empty_location(), AST_Expr, NULL));
|
||||
AST_push_node(node, AST_new_node(empty_location(), AST_Expr, NULL));
|
||||
AST_push_node(node, AST_new_node(empty_location(NULL), AST_Expr, NULL));
|
||||
AST_push_node(node, AST_new_node(empty_location(NULL), AST_Expr, NULL));
|
||||
|
||||
FILE* out = fopen("ast.gv", "w+");
|
||||
// convert this file ^^^^^^
|
||||
|
|
|
@ -7,22 +7,22 @@
|
|||
#include <mem/cache.h>
|
||||
|
||||
void generate_statement(const AST_NODE_PTR stmt) {
|
||||
const AST_NODE_PTR add = AST_new_node(empty_location(), AST_Add, NULL);
|
||||
const AST_NODE_PTR add = AST_new_node(empty_location(NULL), AST_Add, NULL);
|
||||
|
||||
AST_push_node(add, AST_new_node(empty_location(), AST_Int, "3"));
|
||||
AST_push_node(add, AST_new_node(empty_location(), AST_Int, "6"));
|
||||
AST_push_node(add, AST_new_node(empty_location(NULL), AST_Int, "3"));
|
||||
AST_push_node(add, AST_new_node(empty_location(NULL), AST_Int, "6"));
|
||||
|
||||
AST_push_node(stmt, add);
|
||||
}
|
||||
|
||||
void generate_branch(const AST_NODE_PTR stmt) {
|
||||
const AST_NODE_PTR branch = AST_new_node(empty_location(), AST_If, NULL);
|
||||
const AST_NODE_PTR gt = AST_new_node(empty_location(), AST_Greater, NULL);
|
||||
const AST_NODE_PTR branch = AST_new_node(empty_location(NULL), AST_If, NULL);
|
||||
const AST_NODE_PTR gt = AST_new_node(empty_location(NULL), AST_Greater, NULL);
|
||||
|
||||
AST_push_node(branch, gt);
|
||||
|
||||
AST_push_node(gt, AST_new_node(empty_location(), AST_Float, "2.3"));
|
||||
AST_push_node(gt, AST_new_node(empty_location(), AST_Float, "0.79"));
|
||||
AST_push_node(gt, AST_new_node(empty_location(NULL), AST_Float, "2.3"));
|
||||
AST_push_node(gt, AST_new_node(empty_location(NULL), AST_Float, "0.79"));
|
||||
|
||||
AST_push_node(stmt, branch);
|
||||
|
||||
|
@ -33,7 +33,7 @@ int main(void) {
|
|||
mem_init();
|
||||
AST_init();
|
||||
|
||||
const AST_NODE_PTR root = AST_new_node(empty_location(), AST_Stmt, NULL);
|
||||
const AST_NODE_PTR root = AST_new_node(empty_location(NULL), AST_Stmt, NULL);
|
||||
|
||||
generate_branch(root);
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ int main(void) {
|
|||
mem_init();
|
||||
AST_init();
|
||||
|
||||
const AST_NODE_PTR node = AST_new_node(empty_location(), 0, "value");
|
||||
const AST_NODE_PTR node = AST_new_node(empty_location(NULL), 0, "value");
|
||||
|
||||
for (size_t i = 0; i < AST_ELEMENT_COUNT; i++) {
|
||||
// set kind
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
include(CTest)
|
||||
|
||||
include_directories(${PROJECT_SOURCE_DIR}/src)
|
||||
include_directories(PRIVATE ${GLIB_INCLUDE_DIRS})
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_search_module(GLIB REQUIRED IMPORTED_TARGET glib-2.0)
|
||||
link_libraries(PkgConfig::GLIB)
|
||||
|
||||
include_directories(${PROJECT_SOURCE_DIR}/dep/tomlc99)
|
||||
|
||||
# ------------------------------------------------ #
|
||||
# LLVM backend #
|
||||
# ------------------------------------------------ #
|
||||
|
||||
# Fetch LLVM link configuration
|
||||
execute_process(COMMAND llvm-config --libs all
|
||||
OUTPUT_VARIABLE LLVM_LIBS)
|
||||
# Strip whitespace from output
|
||||
string(STRIP "${LLVM_LIBS}" LLVM_LIBS)
|
||||
# Link all targets to LLVM
|
||||
link_libraries(${LLVM_LIBS})
|
||||
|
||||
execute_process(COMMAND llvm-config --includedir
|
||||
OUTPUT_VARIABLE LLVM_INCLUDE_DIR)
|
||||
string(STRIP "${LLVM_INCLUDE_DIR}" LLVM_INCLUDE_DIR)
|
||||
include_directories(${LLVM_INCLUDE_DIR})
|
||||
|
||||
# ------------------------------------------------------- #
|
||||
# CTEST 1
|
||||
# test llvm backend codegen for global variables
|
||||
|
||||
#file(GLOB_RECURSE SOURCE_FILES ${PROJECT_SOURCE_DIR}/src/llvm/*.c)
|
||||
#list(REMOVE_ITEM SOURCE_FILES ${PROJECT_SOURCE_DIR}/src/main.c)
|
||||
#add_executable(global_vars
|
||||
# ${PROJECT_SOURCE_DIR}/src/codegen/backend.c
|
||||
# ${PROJECT_SOURCE_DIR}/src/sys/log.c
|
||||
# ${PROJECT_SOURCE_DIR}/src/sys/col.c
|
||||
# ${PROJECT_SOURCE_DIR}/src/set/set.c
|
||||
# ${PROJECT_SOURCE_DIR}/src/ast/ast.c
|
||||
# ${PROJECT_SOURCE_DIR}/src/cfg/opt.c
|
||||
# ${PROJECT_SOURCE_DIR}/src/io/files.c
|
||||
# ${PROJECT_SOURCE_DIR}/src/mem/cache.c
|
||||
# global_vars.c
|
||||
# ${SOURCE_FILES})
|
||||
#set_target_properties(global_vars
|
||||
# PROPERTIES
|
||||
# OUTPUT_NAME "global_vars"
|
||||
# RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/tests/llvm)
|
||||
#target_link_libraries(global_vars tomlc99)
|
||||
#add_test(NAME global_vars
|
||||
# WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/tests/llvm
|
||||
# COMMAND ${GEMSTONE_BINARY_DIR}/tests/llvm/global_vars)
|
|
@ -0,0 +1,110 @@
|
|||
|
||||
#include <alloca.h>
|
||||
#include <codegen/backend.h>
|
||||
#include <llvm/backend.h>
|
||||
#include <sys/log.h>
|
||||
#include <set/types.h>
|
||||
|
||||
[[gnu::always_inline]]
|
||||
inline Variable* create_variable_decl(const char* name, StorageQualifier qualifier, Type* type) {
|
||||
Variable* variable = alloca(sizeof(Variable));
|
||||
|
||||
variable->name = name;
|
||||
variable->kind = VariableKindDeclaration;
|
||||
variable->nodePtr = NULL;
|
||||
variable->impl.declaration.nodePtr = NULL;
|
||||
variable->impl.declaration.qualifier = qualifier;
|
||||
variable->impl.declaration.type = type;
|
||||
|
||||
return variable;
|
||||
}
|
||||
|
||||
Module* create_module() {
|
||||
Module* module = malloc(sizeof(Module));
|
||||
|
||||
module->boxes = g_hash_table_new(g_str_hash, g_str_equal);
|
||||
module->functions = g_hash_table_new(g_str_hash, g_str_equal);
|
||||
module->imports = g_array_new(FALSE, FALSE, sizeof(Type));
|
||||
module->variables = g_hash_table_new(g_str_hash, g_str_equal);
|
||||
module->types = g_hash_table_new(g_str_hash, g_str_equal);
|
||||
|
||||
Type type_int;
|
||||
type_int.kind = TypeKindPrimitive;
|
||||
type_int.impl.primitive = Int;
|
||||
|
||||
g_hash_table_insert(module->variables, "a", create_variable_decl("a", Global, &type_int));
|
||||
|
||||
Type type_composite;
|
||||
type_composite.kind = TypeKindComposite;
|
||||
type_composite.impl.composite.primitive = Float;
|
||||
type_composite.impl.composite.scale = 2.0;
|
||||
type_composite.impl.composite.sign = Signed;
|
||||
|
||||
g_hash_table_insert(module->variables, "b", create_variable_decl("b", Global, &type_composite));
|
||||
|
||||
Type type_box;
|
||||
type_box.kind = TypeKindBox;
|
||||
type_box.impl.box->member = g_hash_table_new(g_str_hash, g_str_equal);
|
||||
|
||||
BoxMember* member1 = alloca(sizeof(BoxMember));
|
||||
member1->box = NULL;
|
||||
member1->name = "foo";
|
||||
member1->type = alloca(sizeof(Type));
|
||||
*(member1->type) = type_int;
|
||||
|
||||
g_hash_table_insert(type_box.impl.box->member, "foo", member1);
|
||||
|
||||
Type type_half;
|
||||
type_half.kind = TypeKindComposite;
|
||||
type_half.impl.composite.primitive = Float;
|
||||
type_half.impl.composite.scale = 0.5;
|
||||
type_half.impl.composite.sign = Signed;
|
||||
|
||||
BoxMember* member2 = alloca(sizeof(BoxMember));
|
||||
member2->box = NULL;
|
||||
member2->name = "bar";
|
||||
member2->type = alloca(sizeof(Type));
|
||||
*(member2->type) = type_half;
|
||||
|
||||
g_hash_table_insert(type_box.impl.box->member, "bar", member2);
|
||||
|
||||
g_hash_table_insert(module->variables, "c", create_variable_decl("c", Global, &type_box));
|
||||
|
||||
Type type_reference;
|
||||
type_reference.kind = TypeKindReference;
|
||||
type_reference.impl.reference = &type_box;
|
||||
|
||||
g_hash_table_insert(module->variables, "d", create_variable_decl("d", Global, &type_reference));
|
||||
|
||||
return module;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
parse_options(argc, argv);
|
||||
log_init();
|
||||
|
||||
// no need to clean up ;-)
|
||||
Module* module = create_module();
|
||||
|
||||
llvm_backend_init();
|
||||
|
||||
BackendError err;
|
||||
err = init_backend();
|
||||
if (err.kind != Success) {
|
||||
PANIC("%ld: at [%p] %s", err.kind, err.impl.ast_node, err.impl.message);
|
||||
}
|
||||
|
||||
TargetConfig* config = default_target_config();
|
||||
|
||||
err = generate_code(module, config);
|
||||
if (err.kind != Success) {
|
||||
PANIC("%ld: at [%p] %s", err.kind, err.impl.ast_node, err.impl.message);
|
||||
}
|
||||
|
||||
delete_target_config(config);
|
||||
|
||||
err = deinit_backend();
|
||||
if (err.kind != Success) {
|
||||
PANIC("%ld: at [%p] %s", err.kind, err.impl.ast_node, err.impl.message);
|
||||
}
|
||||
}
|
|
@ -4,6 +4,6 @@ include(CTest)
|
|||
# CTEST 1
|
||||
# test if the program successfully reads the project config
|
||||
|
||||
add_test(NAME project_build
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/tests/project
|
||||
COMMAND python ${GEMSTONE_TEST_DIR}/project/test_project.py)
|
||||
#add_test(NAME project_build
|
||||
# WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/tests/project
|
||||
# COMMAND python ${GEMSTONE_TEST_DIR}/project/test_project.py)
|
||||
|
|
|
@ -6,8 +6,8 @@ description = "This is a test project"
|
|||
|
||||
[target.debug]
|
||||
root = "src/main.txt"
|
||||
output = "bin"
|
||||
archive = "archive"
|
||||
output = "bin/debug"
|
||||
archive = "archive/debug"
|
||||
opt = 1
|
||||
print_ast = true
|
||||
print_asm = true
|
||||
|
|
Loading…
Reference in New Issue