Merge pull request #123 from Servostar/integrate-library

Integrate library
This commit is contained in:
servostar 2024-07-02 16:53:34 +02:00 committed by GitHub
commit db501b4b9e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
77 changed files with 7298 additions and 235 deletions

3
.clang-format Normal file
View File

@ -0,0 +1,3 @@
BasedOnStyle: Google
IndentWidth: 4
PointerAlignment: Left

2
.env
View File

@ -1 +1 @@
SDK=0.2.4-alpine-3.19.1 SDK=0.2.5-alpine-3.19.1

1
.gitignore vendored
View File

@ -18,3 +18,4 @@ build
CTestTestfile.cmake CTestTestfile.cmake
DartConfiguration.tcl DartConfiguration.tcl
*.cmake *.cmake
*.ll

View File

@ -72,9 +72,12 @@ add_custom_command(OUTPUT ${YACC_GENERATED_SOURCE_FILE}
# Setup Glib 2.0 # # Setup Glib 2.0 #
# ------------------------------------------------ # # ------------------------------------------------ #
include(FindPkgConfig)
find_package(PkgConfig REQUIRED) find_package(PkgConfig REQUIRED)
pkg_search_module(GLIB REQUIRED IMPORTED_TARGET glib-2.0) pkg_search_module(GLIB REQUIRED IMPORTED_TARGET glib-2.0)
link_libraries(PkgConfig::GLIB)
# ------------------------------------------------ # # ------------------------------------------------ #
# TOML-C99 # # TOML-C99 #
# ------------------------------------------------ # # ------------------------------------------------ #
@ -95,6 +98,30 @@ set_target_properties(tomlc99
include_directories(${PROJECT_SOURCE_DIR}/dep/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 # # Source #
@ -126,10 +153,6 @@ set_target_properties(release
OUTPUT_NAME "gsc" OUTPUT_NAME "gsc"
RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/release) 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 # FIXME: cannot compile with /O2 because of /RTC1 flag
if (MSVC) if (MSVC)
set(RELEASE_FLAGS) set(RELEASE_FLAGS)
@ -165,10 +188,6 @@ set_target_properties(debug
OUTPUT_NAME "gsc" OUTPUT_NAME "gsc"
RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/debug) RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/debug)
target_link_libraries(debug PkgConfig::GLIB)
target_link_libraries(debug tomlc99)
if (MSVC) if (MSVC)
set(DEBUG_FLAGS /DEBUG) set(DEBUG_FLAGS /DEBUG)
else() else()
@ -198,10 +217,6 @@ set_target_properties(check
OUTPUT_NAME "gsc" OUTPUT_NAME "gsc"
RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/check) RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/check)
target_link_libraries(check PkgConfig::GLIB)
target_link_libraries(check tomlc99)
if (MSVC) if (MSVC)
set(CHECK_FLAGS /DEBUG /WX) set(CHECK_FLAGS /DEBUG /WX)
else() else()

View File

@ -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 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 description="docker image for setting up the build pipeline on SDK"
LABEL website="https://github.com/Servostar/gemstone" 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 run-check-test.sh /home/lorang/
COPY --chown=lorang .env /home/lorang/ COPY --chown=lorang .env /home/lorang/
COPY --chown=lorang run-docker-build.sh /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 dep /home/lorang/dep
COPY --chown=lorang lib /home/lorang/lib
COPY --chown=lorang .git /home/lorang/.git COPY --chown=lorang .git /home/lorang/.git
RUN cmake . RUN cmake .

36
dep/lldcl/CMakeLists.txt Normal file
View File

@ -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)

50
dep/lldcl/lldcl.cpp Normal file
View File

@ -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"

18
examples/lib_common.c Normal file
View File

@ -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;
}

6
examples/lib_common.txt Normal file
View File

@ -0,0 +1,6 @@
fun swap(in out float: a, in out float: b) {
float: c = a
a = b
b = c
}

18
examples/lib_fibonacci.c Normal file
View File

@ -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;
}

View File

@ -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
}

25
lib/CMakeLists.txt Normal file
View File

@ -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})

16
lib/build.toml Normal file
View File

@ -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

7
lib/src/bool.gsc Normal file
View File

@ -0,0 +1,7 @@
import "def.gsc"
type unsigned int: bool
static bool: TRUE = 1
static bool: FALSE = 0

14
lib/src/capi.h Normal file
View File

@ -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

38
lib/src/def.gsc Normal file
View File

@ -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

28
lib/src/def/api.h Normal file
View File

@ -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_

View File

@ -0,0 +1,10 @@
//
// Created by servostar on 6/10/24.
//
extern void entry(void);
int main(int argc, char* argv[]) {
entry();
return 0;
}

53
lib/src/io.gsc Normal file
View File

@ -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)

21
lib/src/io/api.h Normal file
View File

@ -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_

75
lib/src/io/impl.c Normal file
View File

@ -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

27
lib/src/mem.gsc Normal file
View File

@ -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)

17
lib/src/mem/api.h Normal file
View File

@ -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_

52
lib/src/mem/impl.c Normal file
View File

@ -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);
}

25
lib/src/os.gsc Normal file
View File

@ -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)

18
lib/src/os/api.h Normal file
View File

@ -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

36
lib/src/os/impl.c Normal file
View File

@ -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);
}

16
lib/src/std.gsc Normal file
View File

@ -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"

View File

@ -17,6 +17,8 @@ if [ ! $? -eq 0 ]; then
exit 1 exit 1
fi fi
sh -c ./run-lib-build.sh
echo "+--------------------------------------+" echo "+--------------------------------------+"
echo "| RUNNING CODE CHECK |" echo "| RUNNING CODE CHECK |"
echo "+--------------------------------------+" echo "+--------------------------------------+"
@ -39,4 +41,4 @@ fi
echo "+--------------------------------------+" echo "+--------------------------------------+"
echo "| COMPLETED CHECK + TESTS SUCCESSFULLY |" echo "| COMPLETED CHECK + TESTS SUCCESSFULLY |"
echo "+--------------------------------------+" echo "+--------------------------------------+"

View File

@ -47,7 +47,7 @@ echo "+--------------------------------------+"
echo "| RUNNING check test |" echo "| RUNNING check test |"
echo "+--------------------------------------+" 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 if [ ! $? -eq 0 ]; then
echo "===> failed to run build or checks" echo "===> failed to run build or checks"
exit 1 exit 1

30
run-lib-build.sh Executable file
View File

@ -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 "+--------------------------------------+"

View File

@ -1,11 +1,11 @@
FROM alpine:3.19.1 FROM alpine:3.19.1
LABEL authors="servostar" 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 description="base image for building the gemstone programming language compiler"
LABEL website="https://github.com/Servostar/gemstone" LABEL website="https://github.com/Servostar/gemstone"
# install dependencies # 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 # create user for build
RUN adduser --disabled-password lorang RUN adduser --disabled-password lorang

View File

@ -19,8 +19,7 @@ struct AST_Node_t *AST_new_node(TokenLocation location, enum AST_SyntaxElement_t
// init to discrete state // init to discrete state
node->parent = NULL; node->parent = NULL;
node->children = NULL; node->children = mem_new_g_array(MemoryNamespaceAst, sizeof(AST_NODE_PTR));
node->child_count = 0;
node->kind = kind; node->kind = kind;
node->value = value; node->value = value;
node->location = location; 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(owner != NULL);
assert(child != 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) { if (owner->children == NULL) {
PANIC("failed to allocate children array of AST node"); 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.col_start = min(owner->location.col_start, child->location.col_start);
owner->location.line_start = min(owner->location.line_start, child->location.line_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); 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) { struct AST_Node_t *AST_get_node(struct AST_Node_t *owner, const size_t idx) {
DEBUG("retrvieng node %d from %p", idx, owner); DEBUG("retrvieng node %d from %p", idx, owner);
assert(owner != NULL); assert(owner != NULL);
assert(owner->children != NULL); assert(owner->children != NULL);
assert(idx < owner->child_count); assert(idx < owner->children->len);
if (owner->children == NULL) { if (owner->children == NULL) {
PANIC("AST owner node has no children"); 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) { if (child == NULL) {
PANIC("child node is 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) { struct AST_Node_t* AST_remove_child(struct AST_Node_t* owner, const size_t idx) {
assert(owner != NULL); assert(owner != NULL);
assert(owner->children != 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; 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; 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(child != NULL);
assert(owner->children != NULL); assert(owner->children != NULL);
for (size_t i = 0; i < owner->child_count; i++) { for (size_t i = 0; i < owner->children->len; i++) {
if (owner->children[i] == child) { if (g_array_index(owner->children, AST_NODE_PTR, i) == child) {
return AST_remove_child(owner, i); return AST_remove_child(owner, i);
} }
} }
@ -221,10 +207,10 @@ void AST_delete_node(struct AST_Node_t *node) {
} }
if (node->children != NULL) { 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 // prevent detach of children node
node->children[i]->parent = NULL; AST_get_node(node, i)->parent = NULL;
AST_delete_node(node->children[i]); AST_delete_node(AST_get_node(node, i));
} }
mem_free(node->children); mem_free(node->children);
} }
@ -242,8 +228,8 @@ static void AST_visit_nodes_recurse2(struct AST_Node_t *root,
(for_each)(root, depth); (for_each)(root, depth);
for (size_t i = 0; i < root->child_count; i++) { for (size_t i = 0; i < root->children->len; i++) {
AST_visit_nodes_recurse2(root->children[i], for_each, depth + 1); 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; return;
} }
for (size_t i = 0; i < node->child_count; i++) { for (size_t i = 0; i < node->children->len; i++) {
AST_fprint_graphviz_node_definition(stream, node->children[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; return;
} }
for (size_t i = 0; i < node->child_count; i++) { for (size_t i = 0; i < node->children->len; i++) {
fprintf(stream, "\tnode%p -- node%p\n", (void*) node, (void*) node->children[i]); AST_NODE_PTR child = g_array_index(node->children, AST_NODE_PTR, i);
AST_fprint_graphviz_node_connection(stream, node->children[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"); 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);
}

View File

@ -89,7 +89,7 @@ enum AST_SyntaxElement_t {
* - kind: The type of the node. Such as AST_Expr, AST_Add, ... * - 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 * - 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 // parent node that owns this node
struct AST_Node_t *parent; struct AST_Node_t *parent;
@ -100,12 +100,9 @@ struct AST_Node_t {
TokenLocation location; TokenLocation location;
// number of child nodes ownd by this node // children array
// length of children array GArray* children;
size_t child_count; } AST_Node;
// variable amount of child nodes
struct AST_Node_t **children;
};
/** /**
* Shorthand type for a single 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)]] [[gnu::nonnull(1)]]
struct AST_Node_t *AST_get_node(struct AST_Node_t *owner, size_t idx); 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 * @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 * @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)]] [[gnu::nonnull(1), gnu::nonnull(2)]]
void AST_fprint_graphviz(FILE* stream, const struct AST_Node_t* node); void AST_fprint_graphviz(FILE* stream, const struct AST_Node_t* node);
#endif 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

View File

@ -103,6 +103,10 @@ TargetConfig* default_target_config() {
config->output_directory = mem_strdup(MemoryNamespaceOpt, "bin"); config->output_directory = mem_strdup(MemoryNamespaceOpt, "bin");
config->optimization_level = 1; config->optimization_level = 1;
config->root_module = NULL; 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; return config;
} }
@ -112,6 +116,16 @@ TargetConfig* default_target_config_from_args() {
TargetConfig* config = default_target_config(); 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")) { if (is_option_set("print-ast")) {
config->print_ast = true; 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"); GArray* files = get_non_options_after("compile");
if (files == NULL) { if (files == NULL) {
@ -161,6 +209,9 @@ TargetConfig* default_target_config_from_args() {
g_array_free(files, TRUE); g_array_free(files, TRUE);
} }
char* default_import_path = mem_strdup(MemoryNamespaceOpt, ".");
g_array_append_val(config->import_paths, default_import_path);
return config; return config;
} }
@ -173,17 +224,23 @@ void print_help(void) {
"Compile non-project file: gsc compile <target-options> [file]", "Compile non-project file: gsc compile <target-options> [file]",
"Output information: gsc <option>", "Output information: gsc <option>",
"Target options:", "Target options:",
" --print-ast print resulting abstract syntax tree to a file", " --print-ast print resulting abstract syntax tree to a file",
" --print-asm print resulting assembly language to a file", " --print-asm print resulting assembly language to a file",
" --print-ir print resulting LLVM-IR to a file", " --print-ir print resulting LLVM-IR to a file",
" --mode=[app|lib] set the compilation mode to either application or library", " --mode=[app|lib] set the compilation mode to either application or library",
" --output=name name of output files without extension", " --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:", "Options:",
" --verbose print logs with level information or higher", " --verbose print logs with level information or higher",
" --debug print debug logs (if not disabled at compile time)", " --debug print debug logs (if not disabled at compile time)",
" --version print the version", " --version print the version",
" --help print this hel dialog", " --list-targets print a list of all available targets supported",
" --print-memory-stats print statistics of the garbage collector" " --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++) { 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->root_module, target_table, "root");
get_str(&target_config->output_directory, target_table, "output"); get_str(&target_config->output_directory, target_table, "output");
get_str(&target_config->archive_directory, target_table, "archive"); 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"); get_int(&target_config->optimization_level, target_table, "opt");
@ -374,6 +433,12 @@ void delete_target_config(TargetConfig* config) {
if (config->output_directory != NULL) { if (config->output_directory != NULL) {
mem_free(config->output_directory); 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); mem_free(config);
} }

View File

@ -16,6 +16,16 @@
#define TOML_ERROR_MSG_BUF 256 #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 { typedef enum TargetCompilationMode_t {
// output an executable binary // output an executable binary
Application, Application,
@ -44,6 +54,14 @@ typedef struct TargetConfig_t {
TargetCompilationMode mode; TargetCompilationMode mode;
// number between 1 and 3 // number between 1 and 3
int optimization_level; 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; } TargetConfig;
/** /**

91
src/codegen/backend.c Normal file
View File

@ -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);
}

96
src/codegen/backend.h Normal file
View File

@ -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_

View File

@ -11,7 +11,12 @@
#include <io/files.h> #include <io/files.h>
#include <assert.h> #include <assert.h>
#include <cfg/opt.h> #include <cfg/opt.h>
#include <codegen/backend.h>
#include <llvm/backend.h>
#include <mem/cache.h> #include <mem/cache.h>
#include <set/set.h>
#define GRAPHVIZ_FILE_EXTENSION "gv"
extern void yyrestart(FILE *); extern void yyrestart(FILE *);
@ -52,14 +57,14 @@ static int compile_file_to_ast(AST_NODE_PTR ast, ModuleFile *file) {
yyrestart(yyin); yyrestart(yyin);
lex_reset(); lex_reset();
yyparse(); int status = yyparse();
// clean up global state // clean up global state
// current_file = NULL; // current_file = NULL;
root = NULL; root = NULL;
yyin = NULL; yyin = NULL;
return EXIT_SUCCESS; return status;
} }
/** /**
@ -93,6 +98,8 @@ static int setup_target_environment(const TargetConfig *target) {
return EXIT_FAILURE; return EXIT_FAILURE;
} }
INFO("setup environment successfully");
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
@ -101,29 +108,134 @@ static int setup_target_environment(const TargetConfig *target) {
* @param ast * @param ast
* @param target * @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(ast != NULL);
assert(target != 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; return;
}
// create file path to write graphviz to // 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) { 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); print_message(Error, "Unable to open file for syntax tree at: %s: %s", path, message);
free((void *) message); free(message);
} else { } else {
DEBUG("writing graph to file...");
AST_fprint_graphviz(output, ast); AST_fprint_graphviz(output, ast);
fclose(output); 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) { static void build_target(ModuleFileStack *unit, const TargetConfig *target) {
print_message(Info, "Building target: %s", target->name); 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); 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 (compile_module_with_dependencies(unit, file, target, root_module) == EXIT_SUCCESS) {
if (setup_target_environment(target) == 0) { 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 if (module != NULL) {
// TODO: backend codegen run_backend_codegen(module, target);
}
}
AST_delete_node(root_module);
} }
} }
AST_delete_node(ast);
mem_purge_namespace(MemoryNamespaceLex); mem_purge_namespace(MemoryNamespaceLex);
mem_purge_namespace(MemoryNamespaceAst); mem_purge_namespace(MemoryNamespaceAst);
mem_purge_namespace(MemoryNamespaceSet);
print_file_statistics(file); print_file_statistics(file);
} }

View File

@ -3,6 +3,8 @@
// //
#include <io/files.h> #include <io/files.h>
#include <stdarg.h>
#include <stdio.h>
#include <sys/log.h> #include <sys/log.h>
#include <assert.h> #include <assert.h>
#include <sys/col.h> #include <sys/col.h>
@ -40,22 +42,24 @@ ModuleFile *push_file(ModuleFileStack *stack, const char *path) {
// lazy init of heap stack // lazy init of heap stack
if (stack->files == NULL) { 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 = { ModuleFile* new_file = mem_alloc(MemoryNamespaceStatic, sizeof(ModuleFile));
.path = path, new_file->handle = NULL;
.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); 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) { void delete_files(ModuleFileStack *stack) {
for (size_t i = 0; i < stack->files->len; i++) { 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) { if (file->handle != NULL) {
DEBUG("closing file: %s", file->path); 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 // behaves like fgets except that it has defined behavior when n == 1
static void custom_fgets(char *buffer, size_t n, FILE *stream) { static void custom_fgets(char *buffer, size_t n, FILE *stream) {
if (feof(stream)) {
buffer[0] = '\n';
buffer[1] = 0;
return;
}
if (n == 1) { if (n == 1) {
buffer[0] = (char) fgetc(stream); buffer[0] = (char) fgetc(stream);
buffer[1] = 0; 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) { void print_diagnostic(TokenLocation *location, Message kind, const char *message, ...) {
assert(file->handle != NULL); assert(location->file != NULL);
assert(location->file->handle != NULL);
assert(location != NULL); assert(location != NULL);
assert(message != NULL); assert(message != NULL);
// reset to start // reset to start
rewind(file->handle); rewind(location->file->handle);
char *buffer = alloca(SEEK_BUF_BYTES); char *buffer = alloca(SEEK_BUF_BYTES);
unsigned long int line_count = 1; unsigned long int line_count = 1;
// seek to first line // 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; line_count += strchr(buffer, '\n') != NULL;
} }
@ -104,24 +114,32 @@ void print_diagnostic(ModuleFile *file, TokenLocation *location, Message kind, c
case Info: case Info:
kind_text = "info"; kind_text = "info";
accent_color = CYAN; accent_color = CYAN;
file->statistics.info_count++; location->file->statistics.info_count++;
break; break;
case Warning: case Warning:
kind_text = "warning"; kind_text = "warning";
accent_color = YELLOW; accent_color = YELLOW;
file->statistics.warning_count++; location->file->statistics.warning_count++;
break; break;
case Error: case Error:
kind_text = "error"; kind_text = "error";
accent_color = RED; accent_color = RED;
file->statistics.error_count++; location->file->statistics.error_count++;
break; 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, printf("%s%s:%ld:%s %s%s:%s ", BOLD, absolute_path, location->line_start, RESET, accent_color, kind_text, RESET);
message);
va_list args;
va_start(args, message);
vprintf(message, args);
va_end(args);
printf("\n");
mem_free((void *) absolute_path); 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 // print line before token group start
unsigned long int limit = min(location->col_start, SEEK_BUF_BYTES); unsigned long int limit = min(location->col_start, SEEK_BUF_BYTES);
while (limit > 1) { while (limit > 1) {
custom_fgets(buffer, (int) limit, file->handle); custom_fgets(buffer, (int) limit, location->file->handle);
chars += printf("%s", buffer); chars += printf("%s", buffer);
limit = min(location->col_start - chars, SEEK_BUF_BYTES); 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; chars = 0;
limit = min(location->col_end - location->col_start + 1, SEEK_BUF_BYTES); limit = min(location->col_end - location->col_start + 1, SEEK_BUF_BYTES);
while (limit > 0) { while (limit > 0) {
custom_fgets(buffer, (int) limit, file->handle); custom_fgets(buffer, (int) limit, location->file->handle);
chars += printf("%s", buffer); chars += printf("%s", buffer);
limit = min(location->col_end - location->col_start + 1 - chars, SEEK_BUF_BYTES); 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 // print rest of the line
do { do {
custom_fgets(buffer, SEEK_BUF_BYTES, file->handle); custom_fgets(buffer, SEEK_BUF_BYTES, location->file->handle);
printf("%s", buffer); printf("%s", buffer);
} while (strchr(buffer, '\n') == NULL); } 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, 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; TokenLocation location;
location.line_start = line_start; location.line_start = line_start;
location.line_end = line_end; location.line_end = line_end;
location.col_start = col_start; location.col_start = col_start;
location.col_end = col_end; location.col_end = col_end;
location.file = file;
return location; return location;
} }
TokenLocation empty_location(void) { TokenLocation empty_location(ModuleFile* file) {
TokenLocation location; TokenLocation location;
location.line_start = 0; location.line_start = 0;
location.line_end = 0; location.line_end = 0;
location.col_start = 0; location.col_start = 0;
location.col_end = 0; location.col_end = 0;
location.file = file;
return location; return location;
} }
@ -236,7 +256,7 @@ void print_unit_statistics(ModuleFileStack *file_stack) {
stats.error_count = 0; stats.error_count = 0;
for (size_t i = 0; i < file_stack->files->len; i++) { 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.info_count += file->statistics.warning_count;
stats.warning_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); DEBUG("creating directory: %s", path);
int result; return g_mkdir_with_parents(path, 0755);
#ifdef __unix__
result = mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
#elif defined(_WIN32) || defined(WIN32)
result = _mkdir(path);
#endif
return result;
} }
const char *get_last_error() { const char *get_last_error() {
@ -316,44 +329,9 @@ const char *get_absolute_path(const char *path) {
DEBUG("resolving absolute path of: %s", path); DEBUG("resolving absolute path of: %s", path);
#ifdef __unix__ char* cwd = g_get_current_dir();
// use unix specific function char* canoical = g_canonicalize_filename(path, cwd);
char absolute_path[MAX_PATH_BYTES]; g_free(cwd);
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
return mem_strdup(MemoryNamespaceIo, absolute_path); return canoical;
}
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;
} }

View File

@ -41,6 +41,7 @@ typedef struct TokenLocation_t {
unsigned long int col_start; unsigned long int col_start;
unsigned long int line_end; unsigned long int line_end;
unsigned long int col_end; unsigned long int col_end;
ModuleFile* file;
} TokenLocation; } TokenLocation;
/** /**
@ -75,24 +76,23 @@ void delete_files(ModuleFileStack *stack);
* @return * @return
*/ */
TokenLocation new_location(unsigned long int line_start, unsigned long int col_start, unsigned long int line_end, 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 * @brief Create a new empty location with all of its contents set to zero
* @return * @return
*/ */
TokenLocation empty_location(void); TokenLocation empty_location(ModuleFile* file);
/** /**
* @brief Prints some diagnostic message to stdout. * @brief Prints some diagnostic message to stdout.
* This also print the token group and the attached source as context. * This also print the token group and the attached source as context.
* @param file
* @param location * @param location
* @param kind * @param kind
* @param message * @param message
*/ */
[[gnu::nonnull(1), gnu::nonnull(2)]] [[gnu::nonnull(1)]]
void print_diagnostic(ModuleFile *file, TokenLocation *location, Message kind, const char *message); void print_diagnostic(TokenLocation *location, Message kind, const char *message, ...);
[[gnu::nonnull(2)]] [[gnu::nonnull(2)]]
/** /**
@ -142,15 +142,4 @@ const char* get_last_error();
[[nodiscard("pointer must be freed")]] [[nodiscard("pointer must be freed")]]
const char* get_absolute_path(const char* path); 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 #endif //GEMSTONE_FILES_H

View File

@ -98,10 +98,13 @@
yytext = yytext +1; yytext = yytext +1;
yytext[yyleng - 2] = 0; 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 = yytext +3;
yytext[yyleng - 4] = 0; yytext[yyleng - 6] = 0;
DEBUG("\"%s\" tokenized with \'ValMultistr\'", yytext); yylval.string = mem_strdup(MemoryNamespaceLex, yytext); return(ValMultistr);}; DEBUG("\"%s\" tokenized with \'ValMultistr\'", yytext); yylval.string = mem_strdup(MemoryNamespaceLex, yytext); return(ValMultistr);};
[ \r\t] { /* ignore whitespace */ }; [ \r\t] { /* ignore whitespace */ };

View File

@ -84,3 +84,41 @@ int getNextLine(void) {
return 0; 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;
}

View File

@ -36,4 +36,6 @@ int nextChar(char *dst);
*/ */
int getNextLine(void); int getNextLine(void);
char* collapse_escape_sequences(char* string);
#endif // LEX_UTIL_H_ #endif // LEX_UTIL_H_

120
src/llvm/backend.c Normal file
View File

@ -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);
}
}

36
src/llvm/backend.h Normal file
View File

@ -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_

149
src/llvm/link/lld.c Normal file
View File

@ -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);
}

17
src/llvm/link/lld.h Normal file
View File

@ -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

471
src/llvm/llvm-ir/expr.c Normal file
View File

@ -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;
}

18
src/llvm/llvm-ir/expr.h Normal file
View File

@ -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

256
src/llvm/llvm-ir/func.c Normal file
View File

@ -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;
}

41
src/llvm/llvm-ir/func.h Normal file
View File

@ -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_

484
src/llvm/llvm-ir/stmt.c Normal file
View File

@ -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(&parameter)) {
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;
}

19
src/llvm/llvm-ir/stmt.h Normal file
View File

@ -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

461
src/llvm/llvm-ir/types.c Normal file
View File

@ -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;
}

26
src/llvm/llvm-ir/types.h Normal file
View File

@ -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_

View File

@ -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;
}

View File

@ -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_

299
src/llvm/parser.c Normal file
View File

@ -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);
}

33
src/llvm/parser.h Normal file
View File

@ -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_

View File

@ -5,6 +5,7 @@
#include <lex/util.h> #include <lex/util.h>
#include <cfg/opt.h> #include <cfg/opt.h>
#include <compiler.h> #include <compiler.h>
#include <llvm/parser.h>
#include <mem/cache.h> #include <mem/cache.h>
/** /**
@ -58,9 +59,14 @@ int main(int argc, char *argv[]) {
exit(0); exit(0);
} }
if (is_option_set("list-targets")) {
list_available_targets();
exit(0);
}
run_compiler(); run_compiler();
if (is_option_set("print-memory-stats")) { if (is_option_set("print-gc-stats")) {
print_memory_statistics(); print_memory_statistics();
} }

View File

@ -7,6 +7,7 @@
#include <glib.h> #include <glib.h>
#include <string.h> #include <string.h>
#include <assert.h> #include <assert.h>
#include <cfg/opt.h>
static GHashTable* namespaces = NULL; static GHashTable* namespaces = NULL;
@ -20,6 +21,17 @@ typedef struct MemoryNamespaceStatistic_t {
size_t purged_free_count; size_t purged_free_count;
} MemoryNamespaceStatistic; } 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 { typedef struct MemoryNamespace_t {
MemoryNamespaceStatistic statistic; MemoryNamespaceStatistic statistic;
GArray* blocks; GArray* blocks;
@ -44,9 +56,11 @@ static void* namespace_malloc(MemoryNamespaceRef memoryNamespace, size_t size) {
assert(memoryNamespace != NULL); assert(memoryNamespace != NULL);
assert(size != 0); 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 ++; memoryNamespace->statistic.faulty_allocations ++;
} else { } else {
g_array_append_val(memoryNamespace->blocks, block); 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; 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) { static gboolean namespace_free(MemoryNamespaceRef memoryNamespace, void* block) {
for (guint i = 0; i < memoryNamespace->blocks->len; i++) { 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); assert(block != NULL);
free(block); namespace_free_block(current_block);
g_array_remove_index(memoryNamespace->blocks, i); g_array_remove_index(memoryNamespace->blocks, i);
memoryNamespace->statistic.manual_free_count++; memoryNamespace->statistic.manual_free_count++;
return TRUE; return TRUE;
} }
} }
@ -79,13 +109,13 @@ static void* namespace_realloc(MemoryNamespaceRef memoryNamespace, void* block,
void* reallocated_block = NULL; void* reallocated_block = NULL;
for (guint i = 0; i < memoryNamespace->blocks->len; i++) { 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) {
reallocated_block = realloc(block, size); reallocated_block = realloc(current_block.block_ptr, size);
if (reallocated_block != NULL) { 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.bytes_allocated += size;
memoryNamespace->statistic.reallocation_count ++; memoryNamespace->statistic.reallocation_count ++;
} else { } else {
@ -107,9 +137,9 @@ static void namespace_delete(MemoryNamespaceRef memoryNamespace) {
static void namespace_purge(MemoryNamespaceRef memoryNamespace) { static void namespace_purge(MemoryNamespaceRef memoryNamespace) {
for (guint i = 0; i < memoryNamespace->blocks->len; i++) { 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 ++; memoryNamespace->statistic.purged_free_count ++;
} }
@ -120,7 +150,7 @@ static void namespace_purge(MemoryNamespaceRef memoryNamespace) {
static MemoryNamespaceRef namespace_new() { static MemoryNamespaceRef namespace_new() {
MemoryNamespaceRef memoryNamespace = malloc(sizeof(MemoryNamespace)); 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.bytes_allocated = 0;
memoryNamespace->statistic.allocation_count = 0; memoryNamespace->statistic.allocation_count = 0;
memoryNamespace->statistic.manual_free_count = 0; memoryNamespace->statistic.manual_free_count = 0;
@ -132,6 +162,30 @@ static MemoryNamespaceRef namespace_new() {
return memoryNamespace; 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() { static void cleanup() {
if (namespaces == NULL) { if (namespaces == NULL) {
printf("==> Memory cache was unused <==\n"); printf("==> Memory cache was unused <==\n");
@ -221,7 +275,7 @@ void mem_purge_namespace(MemoryNamespaceName name) {
namespace_purge(cache); namespace_purge(cache);
} else { } 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"); 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);
} }

View File

@ -7,6 +7,7 @@
#include <mem/cache.h> #include <mem/cache.h>
#include <stddef.h> #include <stddef.h>
#include <glib.h>
typedef char* MemoryNamespaceName; typedef char* MemoryNamespaceName;
@ -16,6 +17,7 @@ typedef char* MemoryNamespaceName;
#define MemoryNamespaceOpt "Options" #define MemoryNamespaceOpt "Options"
#define MemoryNamespaceSet "SET" #define MemoryNamespaceSet "SET"
#define MemoryNamespaceLlvm "LLVM" #define MemoryNamespaceLlvm "LLVM"
#define MemoryNamespaceLld "LLD"
#define MemoryNamespaceIo "I/O" #define MemoryNamespaceIo "I/O"
#define MemoryNamespaceStatic "Static" #define MemoryNamespaceStatic "Static"
@ -86,4 +88,8 @@ void* mem_clone(MemoryNamespaceName name, void* data, size_t size);
void print_memory_statistics(); 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 #endif //GEMSTONE_CACHE_H

2467
src/set/set.c Normal file

File diff suppressed because it is too large Load Diff

19
src/set/set.h Normal file
View File

@ -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

620
src/set/types.h Normal file
View File

@ -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_

View File

@ -3,6 +3,7 @@
#include <string.h> #include <string.h>
#include <sys/col.h> #include <sys/col.h>
#include <sys/log.h> #include <sys/log.h>
#include <cfg/opt.h>
#ifdef __unix__ #ifdef __unix__
#include <unistd.h> #include <unistd.h>
@ -55,6 +56,10 @@ void enable_ansi_colors() {
int stdout_supports_ansi_esc() { int stdout_supports_ansi_esc() {
if (is_option_set("color-always")) {
return ANSI_ENABLED;
}
#ifdef __unix__ #ifdef __unix__
// check if TTY // check if TTY
if (isatty(STDOUT_FILENO)) { if (isatty(STDOUT_FILENO)) {

View File

@ -17,7 +17,7 @@
extern int yylex(); extern int yylex();
extern AST_NODE_PTR root; 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 { %union {
@ -55,6 +55,7 @@
%type <node_ptr> moduleimport %type <node_ptr> moduleimport
%type <node_ptr> programbody %type <node_ptr> programbody
%type <node_ptr> fundef %type <node_ptr> fundef
%type <node_ptr> fundecl
%type <node_ptr> box %type <node_ptr> box
%type <node_ptr> typedef %type <node_ptr> typedef
%type <node_ptr> exprlist %type <node_ptr> exprlist
@ -68,6 +69,7 @@
%type <node_ptr> typecast %type <node_ptr> typecast
%type <node_ptr> reinterpretcast %type <node_ptr> reinterpretcast
%type <node_ptr> program %type <node_ptr> program
%type <node_ptr> storage_expr
%token KeyInt %token KeyInt
@ -135,11 +137,13 @@
%left '(' ')' '[' ']' %left '(' ')' '[' ']'
%% %%
program: program programbody {AST_push_node(root, $2);} program: program programbody {AST_push_node(root, $2);
}
| programbody {AST_push_node(root, $1);}; | programbody {AST_push_node(root, $1);};
programbody: moduleimport {$$ = $1;} programbody: moduleimport {$$ = $1;}
| fundef{$$ = $1;} | fundef{$$ = $1;}
| fundecl{$$ = $1;}
| box{$$ = $1;} | box{$$ = $1;}
| definition{$$ = $1;} | definition{$$ = $1;}
| decl{$$ = $1;} | decl{$$ = $1;}
@ -190,6 +194,13 @@ fundef: KeyFun Ident paramlist '{' statementlist'}' {AST_NODE_PTR fun = AST_new_
$$ = fun; $$ = fun;
DEBUG("Function");}; 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); paramlist: paramlist '(' params ')' {AST_push_node($1, $3);
$$ = $1;} $$ = $1;}
| paramlist '(' ')'{$$ = $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, $1);
AST_push_node(decl, $2); AST_push_node(decl, $2);
AST_push_node(decl, $4); AST_push_node(decl, $4);
$$ = decl;} $$ = decl;};
definition: decl '=' expr { AST_NODE_PTR def = AST_new_node(new_loc(), AST_Def, NULL); 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");} | KeyStatic {$$ = AST_new_node(new_loc(), AST_Storage, "static");}
| KeyLocal {$$ = AST_new_node(new_loc(), AST_Storage, "local");}; | KeyLocal {$$ = AST_new_node(new_loc(), AST_Storage, "local");};
assign: Ident '=' expr { AST_NODE_PTR assign = AST_new_node(new_loc(), AST_Assign, NULL); assign: storage_expr '=' 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, $1);
AST_push_node(assign, ident);
AST_push_node(assign, $3); AST_push_node(assign, $3);
$$ = assign; $$ = assign; };
DEBUG("Assignment"); }
storage_expr: Ident { $$ = AST_new_node(new_loc(), AST_Ident, $1); }
| boxaccess '=' expr | boxaccess { $$ = $1; }
| boxselfaccess '=' expr ; | 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");} sign: KeySigned {$$ = AST_new_node(new_loc(), AST_Sign, "signed");}
| KeyUnsigned{$$ = AST_new_node(new_loc(), AST_Sign, "unsigned");}; | 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) { int yyerror(const char *s) {
TokenLocation location = new_loc(); TokenLocation location = new_loc();
print_diagnostic(current_file, &location, Error, s); print_diagnostic(&location, Error, s);
return 0; return 0;
} }

View File

@ -10,5 +10,6 @@ add_subdirectory(logging)
add_subdirectory(input_file) add_subdirectory(input_file)
add_subdirectory(ast) add_subdirectory(ast)
add_subdirectory(glib) add_subdirectory(glib)
add_subdirectory(llvm)
add_subdirectory(project) add_subdirectory(project)
add_subdirectory(cache) add_subdirectory(cache)

View File

@ -7,22 +7,22 @@
#include <mem/cache.h> #include <mem/cache.h>
void generate_statement(const AST_NODE_PTR stmt) { 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(NULL), 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, "6"));
AST_push_node(stmt, add); AST_push_node(stmt, add);
} }
void generate_branch(const AST_NODE_PTR stmt) { 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 branch = AST_new_node(empty_location(NULL), AST_If, NULL);
const AST_NODE_PTR gt = AST_new_node(empty_location(), AST_Greater, NULL); const AST_NODE_PTR gt = AST_new_node(empty_location(NULL), AST_Greater, NULL);
AST_push_node(branch, gt); 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(NULL), 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, "0.79"));
AST_push_node(stmt, branch); AST_push_node(stmt, branch);
@ -32,7 +32,7 @@ void generate_branch(const AST_NODE_PTR stmt) {
int main(void) { int main(void) {
mem_init(); 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); generate_branch(root);

View File

@ -9,15 +9,15 @@
int main(void) { int main(void) {
mem_init(); 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); struct AST_Node_t* child = AST_new_node(empty_location(NULL), AST_Add, NULL);
AST_push_node(child, AST_new_node(empty_location(), AST_Int, "43")); AST_push_node(child, AST_new_node(empty_location(NULL), AST_Int, "43"));
AST_push_node(child, AST_new_node(empty_location(), AST_Int, "9")); AST_push_node(child, AST_new_node(empty_location(NULL), AST_Int, "9"));
AST_push_node(node, child); 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(NULL), 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));
FILE* out = fopen("ast.gv", "w+"); FILE* out = fopen("ast.gv", "w+");
// convert this file ^^^^^^ // convert this file ^^^^^^

View File

@ -7,22 +7,22 @@
#include <mem/cache.h> #include <mem/cache.h>
void generate_statement(const AST_NODE_PTR stmt) { 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(NULL), 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, "6"));
AST_push_node(stmt, add); AST_push_node(stmt, add);
} }
void generate_branch(const AST_NODE_PTR stmt) { 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 branch = AST_new_node(empty_location(NULL), AST_If, NULL);
const AST_NODE_PTR gt = AST_new_node(empty_location(), AST_Greater, NULL); const AST_NODE_PTR gt = AST_new_node(empty_location(NULL), AST_Greater, NULL);
AST_push_node(branch, gt); 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(NULL), 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, "0.79"));
AST_push_node(stmt, branch); AST_push_node(stmt, branch);
@ -33,7 +33,7 @@ int main(void) {
mem_init(); mem_init();
AST_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); generate_branch(root);

View File

@ -9,7 +9,7 @@ int main(void) {
mem_init(); mem_init();
AST_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++) { for (size_t i = 0; i < AST_ELEMENT_COUNT; i++) {
// set kind // set kind

53
tests/llvm/CMakeLists.txt Normal file
View File

@ -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)

110
tests/llvm/global_vars.c Normal file
View File

@ -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);
}
}

View File

@ -4,6 +4,6 @@ include(CTest)
# CTEST 1 # CTEST 1
# test if the program successfully reads the project config # test if the program successfully reads the project config
add_test(NAME project_build #add_test(NAME project_build
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/tests/project # WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/tests/project
COMMAND python ${GEMSTONE_TEST_DIR}/project/test_project.py) # COMMAND python ${GEMSTONE_TEST_DIR}/project/test_project.py)

View File

@ -6,8 +6,8 @@ description = "This is a test project"
[target.debug] [target.debug]
root = "src/main.txt" root = "src/main.txt"
output = "bin" output = "bin/debug"
archive = "archive" archive = "archive/debug"
opt = 1 opt = 1
print_ast = true print_ast = true
print_asm = true print_asm = true