mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
LLVM Codegen
This commit is contained in:
145
CMakeLists.txt
145
CMakeLists.txt
@@ -1,8 +1,134 @@
|
|||||||
cmake_minimum_required(VERSION 3.13)
|
cmake_minimum_required(VERSION 3.13)
|
||||||
project(c3c C)
|
project(c3c)
|
||||||
set(CMAKE_CXX_FLAGS_RELEASE "-O3")
|
#set(CMAKE_CXX_FLAGS_RELEASE "-O3")
|
||||||
|
|
||||||
|
find_package(LLVM REQUIRED CONFIG)
|
||||||
|
|
||||||
|
message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
|
||||||
|
message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
|
||||||
|
|
||||||
|
include_directories(${LLVM_INCLUDE_DIRS})
|
||||||
|
add_definitions(${LLVM_DEFINITIONS})
|
||||||
|
|
||||||
set(CMAKE_C_STANDARD 11)
|
set(CMAKE_C_STANDARD 11)
|
||||||
|
|
||||||
|
set(LLVM_LINK_COMPONENTS
|
||||||
|
Analysis
|
||||||
|
BitReader
|
||||||
|
Core
|
||||||
|
ExecutionEngine
|
||||||
|
InstCombine
|
||||||
|
Interpreter
|
||||||
|
MC
|
||||||
|
MCDisassembler
|
||||||
|
MCJIT
|
||||||
|
Object
|
||||||
|
OrcJIT
|
||||||
|
RuntimeDyld
|
||||||
|
ScalarOpts
|
||||||
|
Support
|
||||||
|
Target
|
||||||
|
TransformUtils
|
||||||
|
native
|
||||||
|
nativecodegen
|
||||||
|
AsmPrinter
|
||||||
|
AArch64Info
|
||||||
|
AArch64Desc
|
||||||
|
AArch64AsmParser
|
||||||
|
AArch64Disassembler
|
||||||
|
AArch64CodeGen
|
||||||
|
ARMInfo
|
||||||
|
ARMDesc
|
||||||
|
ARMAsmParser
|
||||||
|
ARMDisassembler
|
||||||
|
ARMCodeGen
|
||||||
|
AMDGPUInfo
|
||||||
|
AMDGPUAsmParser
|
||||||
|
AMDGPUCodeGen
|
||||||
|
BPFInfo
|
||||||
|
BPFAsmParser
|
||||||
|
BPFDisassembler
|
||||||
|
BPFCodeGen
|
||||||
|
HexagonInfo
|
||||||
|
HexagonDesc
|
||||||
|
HexagonAsmParser
|
||||||
|
HexagonDisassembler
|
||||||
|
HexagonCodeGen
|
||||||
|
LanaiInfo
|
||||||
|
LanaiDesc
|
||||||
|
LanaiAsmParser
|
||||||
|
LanaiDisassembler
|
||||||
|
LanaiCodeGen
|
||||||
|
MSP430Info
|
||||||
|
MSP430Desc
|
||||||
|
MSP430AsmParser
|
||||||
|
MSP430Disassembler
|
||||||
|
MSP430CodeGen
|
||||||
|
MipsInfo
|
||||||
|
MipsDesc
|
||||||
|
MipsAsmParser
|
||||||
|
MipsDisassembler
|
||||||
|
MipsCodeGen
|
||||||
|
NVPTXInfo
|
||||||
|
NVPTXCodeGen
|
||||||
|
PowerPCInfo
|
||||||
|
PowerPCDesc
|
||||||
|
PowerPCAsmParser
|
||||||
|
PowerPCDisassembler
|
||||||
|
PowerPCCodeGen
|
||||||
|
RISCVInfo
|
||||||
|
RISCVDesc
|
||||||
|
RISCVAsmParser
|
||||||
|
RISCVDisassembler
|
||||||
|
RISCVCodeGen
|
||||||
|
RISCVUtils
|
||||||
|
SparcInfo
|
||||||
|
SparcDesc
|
||||||
|
SparcAsmParser
|
||||||
|
SparcDisassembler
|
||||||
|
SparcCodeGen
|
||||||
|
SystemZInfo
|
||||||
|
SystemZDesc
|
||||||
|
SystemZAsmParser
|
||||||
|
SystemZDisassembler
|
||||||
|
SystemZCodeGen
|
||||||
|
WebAssemblyInfo
|
||||||
|
WebAssemblyDesc
|
||||||
|
WebAssemblyAsmParser
|
||||||
|
WebAssemblyDisassembler
|
||||||
|
WebAssemblyCodeGen
|
||||||
|
X86Info
|
||||||
|
X86Desc
|
||||||
|
X86AsmParser
|
||||||
|
X86Disassembler
|
||||||
|
X86CodeGen
|
||||||
|
X86Utils
|
||||||
|
XCoreInfo
|
||||||
|
XCoreDesc
|
||||||
|
XCoreDisassembler
|
||||||
|
XCoreCodeGen
|
||||||
|
)
|
||||||
|
# XRay WindowsManifest Symbolize JITLink MCA LTO Passes ObjCARCOpts
|
||||||
|
#LineEditor LibDriver Interpreter FuzzMutate MCJIT ExecutionEngine
|
||||||
|
#RuntimeDyld DlltoolDriver Option DebugInfoGSYM Coverage Coroutines
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# NVPTXDesc
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# H
|
||||||
|
#BPFDesc ARMUtils
|
||||||
|
#AMDGPUDisassembler MIRParser ipo Instrumentation
|
||||||
|
#Vectorize Linker IRReader AsmParser AMDGPUDesc AMDGPUUtils
|
||||||
|
# MCDisassembler GlobalISel SelectionDAG
|
||||||
|
# DebugInfoDWARF CodeGen Target ScalarOpts InstCombine
|
||||||
|
#AggressiveInstCombine TransformUtils BitWriter Analysis ProfileData Object
|
||||||
|
#TextAPI BitReader Core Remarks BitstreamReader AArch64AsmParser MCParser
|
||||||
|
# MC DebugInfoCodeView DebugInfoMSF BinaryFormat AArch64Utils
|
||||||
|
# Support Demangle
|
||||||
|
llvm_map_components_to_libnames(llvm_libs support core irreader ${LLVM_LINK_COMPONENTS})
|
||||||
|
|
||||||
include_directories(
|
include_directories(
|
||||||
"${CMAKE_SOURCE_DIR}/src/"
|
"${CMAKE_SOURCE_DIR}/src/"
|
||||||
"${CMAKE_SOURCE_DIR}/build/")
|
"${CMAKE_SOURCE_DIR}/build/")
|
||||||
@@ -25,6 +151,19 @@ add_executable(c3c
|
|||||||
src/compiler/source_file.c
|
src/compiler/source_file.c
|
||||||
src/compiler/diagnostics.c
|
src/compiler/diagnostics.c
|
||||||
src/compiler/ast.c
|
src/compiler/ast.c
|
||||||
src/compiler/bigint.c src/compiler/bigint.h src/compiler/context.c src/compiler/codegen.c src/compiler/expr_analysis.c src/compiler/enums.h src/compiler/casts.c src/compiler/compiler.h src/compiler/types.c src/compiler/module.c)
|
src/compiler/bigint.c
|
||||||
|
src/compiler/bigint.h
|
||||||
|
src/compiler/context.c
|
||||||
|
src/compiler/codegen.c
|
||||||
|
src/compiler/expr_analysis.c
|
||||||
|
src/compiler/enums.h
|
||||||
|
src/compiler/casts.c
|
||||||
|
src/compiler/target.c
|
||||||
|
src/compiler/compiler.h
|
||||||
|
src/compiler/types.c
|
||||||
|
src/compiler/module.c
|
||||||
|
src/compiler/llvm_codegen.c src/utils/stringutils.c src/compiler/dwarf.h src/compiler/llvm_codegen_stmt.c src/compiler/llvm_codegen_internal.h src/compiler/llvm_codegen_expr.c src/compiler/llvm_codegen_debug_info.c src/compiler/llvm_codegen_module.c src/compiler/llvm_codegen_type.c src/compiler/llvm_codegen_function.c)
|
||||||
|
|
||||||
target_compile_options(c3c PRIVATE -Werror -Wall -Wextra -Wno-unused-function -Wno-unused-variable -Wno-unused-parameter)
|
target_compile_options(c3c PRIVATE -Werror -Wall -Wextra -Wno-unused-function -Wno-unused-variable -Wno-unused-parameter)
|
||||||
|
|
||||||
|
target_link_libraries(c3c ${llvm_libs})
|
||||||
48
resources/testfragments/super_simple.c3
Normal file
48
resources/testfragments/super_simple.c3
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
module bar;
|
||||||
|
|
||||||
|
func void test(int x)
|
||||||
|
{
|
||||||
|
int i = x;
|
||||||
|
switch (i + 1)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
i = i + 1;
|
||||||
|
next;
|
||||||
|
case 3:
|
||||||
|
case 2:
|
||||||
|
i = i + 2;
|
||||||
|
case 5:
|
||||||
|
default:
|
||||||
|
i = i * 100;
|
||||||
|
case 7:
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
func int test3()
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
typedef func void(int) as Foo;
|
||||||
|
//typedef int as Foo;
|
||||||
|
|
||||||
|
func void test2(int* x, int y, int z)
|
||||||
|
{
|
||||||
|
int i = 3;
|
||||||
|
uint ui = 2;
|
||||||
|
int j = 129;
|
||||||
|
float f = 23.2;
|
||||||
|
f = f + 1.0;
|
||||||
|
f = f - 1.0;
|
||||||
|
f = f * 2.0;
|
||||||
|
f = f / 3.0;
|
||||||
|
i = i * 2;
|
||||||
|
ui = ui * 2;
|
||||||
|
i = i / 2;
|
||||||
|
ui = ui / 2;
|
||||||
|
i = i + 1;
|
||||||
|
ui = ui + 1;
|
||||||
|
i = i - 1;
|
||||||
|
ui = ui - 1;
|
||||||
|
// TODO x + 1;
|
||||||
|
}
|
||||||
@@ -13,7 +13,6 @@
|
|||||||
|
|
||||||
#include "../utils/errors.h"
|
#include "../utils/errors.h"
|
||||||
|
|
||||||
static const char* DEFAULT_TARGET = "default";
|
|
||||||
static const int DEFAULT_SYMTAB_SIZE = 64 * 1024;
|
static const int DEFAULT_SYMTAB_SIZE = 64 * 1024;
|
||||||
static const int MAX_SYMTAB_SIZE = 1024 * 1024;
|
static const int MAX_SYMTAB_SIZE = 1024 * 1024;
|
||||||
|
|
||||||
@@ -52,6 +51,11 @@ static void usage(void)
|
|||||||
OUTPUT(" --symtab <value> - Sets the preferred symtab size.");
|
OUTPUT(" --symtab <value> - Sets the preferred symtab size.");
|
||||||
OUTPUT(" -E - Lex only.");
|
OUTPUT(" -E - Lex only.");
|
||||||
OUTPUT(" -P - Only parse and output the AST as S-expressions.");
|
OUTPUT(" -P - Only parse and output the AST as S-expressions.");
|
||||||
|
OUTPUT(" -O0 - Optimizations off.");
|
||||||
|
OUTPUT(" -O1 - Simple optimizations only.");
|
||||||
|
OUTPUT(" -O2 - Default optimization level.");
|
||||||
|
OUTPUT(" -Os - Optimize for size.");
|
||||||
|
OUTPUT(" -O3 - Aggressive optimization.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -117,7 +121,7 @@ static void parse_optional_target()
|
|||||||
{
|
{
|
||||||
if (at_end() || next_is_opt())
|
if (at_end() || next_is_opt())
|
||||||
{
|
{
|
||||||
build_options.target = DEFAULT_TARGET;
|
build_options.target = NULL;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -199,6 +203,32 @@ static void parse_option()
|
|||||||
{
|
{
|
||||||
case 'h':
|
case 'h':
|
||||||
break;
|
break;
|
||||||
|
case 'O':
|
||||||
|
if (build_options.optimization_level != OPTIMIZATION_NOT_SET)
|
||||||
|
{
|
||||||
|
FAIL_WITH_ERR("Multiple optimization levels were set.");
|
||||||
|
}
|
||||||
|
if (match_shortopt("O0"))
|
||||||
|
{
|
||||||
|
build_options.optimization_level = OPTIMIZATION_NONE;
|
||||||
|
}
|
||||||
|
else if (match_shortopt("O1"))
|
||||||
|
{
|
||||||
|
build_options.optimization_level = OPTIMIZATION_LESS;
|
||||||
|
}
|
||||||
|
else if (match_shortopt("O2") || match_shortopt("Os"))
|
||||||
|
{
|
||||||
|
build_options.optimization_level = OPTIMIZATION_DEFAULT;
|
||||||
|
}
|
||||||
|
else if (match_shortopt("O3"))
|
||||||
|
{
|
||||||
|
build_options.optimization_level = OPTIMIZATION_AGGRESSIVE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
FAIL_WITH_ERR("Invalid optimization level.");
|
||||||
|
}
|
||||||
|
return;
|
||||||
case 'E':
|
case 'E':
|
||||||
if (build_options.compile_option != COMPILE_NORMAL)
|
if (build_options.compile_option != COMPILE_NORMAL)
|
||||||
{
|
{
|
||||||
@@ -270,6 +300,8 @@ void parse_arguments(int argc, const char *argv[])
|
|||||||
build_options.clonglong_size = sizeof(long long);
|
build_options.clonglong_size = sizeof(long long);
|
||||||
build_options.pointer_size = sizeof(void *);
|
build_options.pointer_size = sizeof(void *);
|
||||||
build_options.path = ".";
|
build_options.path = ".";
|
||||||
|
build_options.optimization_level = OPTIMIZATION_NOT_SET;
|
||||||
|
build_options.debug_info = true;
|
||||||
build_options.command = COMMAND_MISSING;
|
build_options.command = COMMAND_MISSING;
|
||||||
build_options.symtab_size = DEFAULT_SYMTAB_SIZE;
|
build_options.symtab_size = DEFAULT_SYMTAB_SIZE;
|
||||||
build_options.files = VECNEW(const char *, MAX_FILES);
|
build_options.files = VECNEW(const char *, MAX_FILES);
|
||||||
@@ -312,4 +344,8 @@ void parse_arguments(int argc, const char *argv[])
|
|||||||
{
|
{
|
||||||
FAIL_WITH_ERR("No command found.");
|
FAIL_WITH_ERR("No command found.");
|
||||||
}
|
}
|
||||||
|
if (build_options.optimization_level == OPTIMIZATION_NOT_SET)
|
||||||
|
{
|
||||||
|
build_options.optimization_level = OPTIMIZATION_DEFAULT;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,6 +69,15 @@ typedef enum
|
|||||||
COMPILE_OUTPUT_AST,
|
COMPILE_OUTPUT_AST,
|
||||||
} CompileOption;
|
} CompileOption;
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
OPTIMIZATION_NOT_SET,
|
||||||
|
OPTIMIZATION_NONE,
|
||||||
|
OPTIMIZATION_LESS,
|
||||||
|
OPTIMIZATION_DEFAULT,
|
||||||
|
OPTIMIZATION_AGGRESSIVE
|
||||||
|
} OptimizationLevel;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
const char* lib_dir[MAX_LIB_DIRS];
|
const char* lib_dir[MAX_LIB_DIRS];
|
||||||
@@ -77,6 +86,7 @@ typedef struct
|
|||||||
const char* project_name;
|
const char* project_name;
|
||||||
const char* target;
|
const char* target;
|
||||||
const char* path;
|
const char* path;
|
||||||
|
const char* cpu;
|
||||||
CompilerCommand command;
|
CompilerCommand command;
|
||||||
uint32_t symtab_size;
|
uint32_t symtab_size;
|
||||||
CompileOption compile_option;
|
CompileOption compile_option;
|
||||||
@@ -87,6 +97,8 @@ typedef struct
|
|||||||
int clong_size;
|
int clong_size;
|
||||||
int clonglong_size;
|
int clonglong_size;
|
||||||
int clongdouble_size;
|
int clongdouble_size;
|
||||||
|
OptimizationLevel optimization_level;
|
||||||
|
bool debug_info;
|
||||||
} BuildOptions;
|
} BuildOptions;
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -99,20 +99,62 @@ Type* type_get_unsigned(Type *type)
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
BinaryOp binary_op[256] = {
|
||||||
|
[TOKEN_STAR] = BINARYOP_MULT,
|
||||||
|
[TOKEN_DIV] = BINARYOP_DIV,
|
||||||
|
[TOKEN_PLUS] = BINARYOP_ADD,
|
||||||
|
[TOKEN_MINUS] = BINARYOP_SUB,
|
||||||
|
[TOKEN_MOD] = BINARYOP_MOD,
|
||||||
|
[TOKEN_SHL] = BINARYOP_SHL,
|
||||||
|
[TOKEN_SHR] = BINARYOP_SHR,
|
||||||
|
[TOKEN_AND] = BINARYOP_AND,
|
||||||
|
[TOKEN_OR] = BINARYOP_OR,
|
||||||
|
[TOKEN_AMP] = BINARYOP_BIT_AND,
|
||||||
|
[TOKEN_BIT_OR] = BINARYOP_BIT_OR,
|
||||||
|
[TOKEN_BIT_XOR] = BINARYOP_BIT_XOR,
|
||||||
|
[TOKEN_EQEQ] = BINARYOP_EQ,
|
||||||
|
[TOKEN_NOT_EQUAL] = BINARYOP_NE,
|
||||||
|
[TOKEN_LESS] = BINARYOP_LT,
|
||||||
|
[TOKEN_LESS_EQ] = BINARYOP_LE,
|
||||||
|
[TOKEN_GREATER] = BINARYOP_GT,
|
||||||
|
[TOKEN_GREATER_EQ] = BINARYOP_GE,
|
||||||
|
[TOKEN_EQ] = BINARYOP_ASSIGN,
|
||||||
|
[TOKEN_MULT_ASSIGN] = BINARYOP_MULT_ASSIGN,
|
||||||
|
[TOKEN_PLUS_ASSIGN] = BINARYOP_ADD_ASSIGN,
|
||||||
|
[TOKEN_MINUS_ASSIGN] = BINARYOP_SUB_ASSIGN,
|
||||||
|
[TOKEN_DIV_ASSIGN] = BINARYOP_DIV_ASSIGN,
|
||||||
|
[TOKEN_MOD_ASSIGN] = BINARYOP_MOD_ASSIGN,
|
||||||
|
[TOKEN_AND_ASSIGN] = BINARYOP_AND_ASSIGN,
|
||||||
|
[TOKEN_OR_ASSIGN] = BINARYOP_OR_ASSIGN,
|
||||||
|
[TOKEN_BIT_AND_ASSIGN] = BINARYOP_BIT_AND_ASSIGN,
|
||||||
|
[TOKEN_BIT_OR_ASSIGN] = BINARYOP_BIT_OR_ASSIGN,
|
||||||
|
[TOKEN_BIT_XOR_ASSIGN] = BINARYOP_BIT_XOR_ASSIGN,
|
||||||
|
[TOKEN_SHR_ASSIGN] = BINARYOP_SHR_ASSIGN,
|
||||||
|
[TOKEN_SHL_ASSIGN] = BINARYOP_SHL_ASSIGN,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static BinaryOp assign_binop[256] = {
|
||||||
|
[BINARYOP_MULT_ASSIGN] = BINARYOP_MULT,
|
||||||
|
[BINARYOP_ADD_ASSIGN] = BINARYOP_ADD,
|
||||||
|
[BINARYOP_SUB_ASSIGN] = BINARYOP_SUB,
|
||||||
|
[BINARYOP_DIV_ASSIGN] = BINARYOP_DIV,
|
||||||
|
[BINARYOP_MOD_ASSIGN] = BINARYOP_MOD,
|
||||||
|
[BINARYOP_AND_ASSIGN] = BINARYOP_AND,
|
||||||
|
[BINARYOP_OR_ASSIGN] = BINARYOP_OR,
|
||||||
|
[BINARYOP_BIT_AND_ASSIGN] = BINARYOP_BIT_AND,
|
||||||
|
[BINARYOP_BIT_OR_ASSIGN] = BINARYOP_BIT_OR,
|
||||||
|
[BINARYOP_BIT_XOR_ASSIGN] = BINARYOP_BIT_XOR,
|
||||||
|
[BINARYOP_SHR_ASSIGN] = BINARYOP_SHR,
|
||||||
|
[BINARYOP_SHL_ASSIGN] = BINARYOP_SHL,
|
||||||
|
};
|
||||||
|
|
||||||
|
BinaryOp binaryop_assign_base_op(BinaryOp assign_binary_op)
|
||||||
|
{
|
||||||
|
return assign_binop[(int)binary_op];
|
||||||
|
}
|
||||||
|
|
||||||
AssignOp assign_op[256] = {
|
AssignOp assign_op[256] = {
|
||||||
[TOKEN_EQ] = ASSIGNOP_ASSIGN,
|
|
||||||
[TOKEN_MULT_ASSIGN] = ASSIGNOP_MULT_ASSIGN,
|
|
||||||
[TOKEN_PLUS_ASSIGN] = ASSIGNOP_ADD_ASSIGN,
|
|
||||||
[TOKEN_MINUS_ASSIGN] = ASSIGNOP_SUB_ASSIGN,
|
|
||||||
[TOKEN_DIV_ASSIGN] = ASSIGNOP_DIV_ASSIGN,
|
|
||||||
[TOKEN_MOD_ASSIGN] = ASSIGNOP_MOD_ASSIGN,
|
|
||||||
[TOKEN_AND_ASSIGN] = ASSIGNOP_AND_ASSIGN,
|
|
||||||
[TOKEN_OR_ASSIGN] = ASSIGNOP_OR_ASSIGN,
|
|
||||||
[TOKEN_BIT_AND_ASSIGN] = ASSIGNOP_BIT_AND_ASSIGN,
|
|
||||||
[TOKEN_BIT_OR_ASSIGN] = ASSIGNOP_BIT_OR_ASSIGN,
|
|
||||||
[TOKEN_BIT_XOR_ASSIGN] = ASSIGNOP_BIT_XOR_ASSIGN,
|
|
||||||
[TOKEN_SHR_ASSIGN] = ASSIGNOP_SHR_ASSIGN,
|
|
||||||
[TOKEN_SHL_ASSIGN] = ASSIGNOP_SHL_ASSIGN,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
UnaryOp unary_op[256] = {
|
UnaryOp unary_op[256] = {
|
||||||
@@ -141,6 +183,20 @@ TokenType assignop_to_token(AssignOp type)
|
|||||||
return TOKEN_INVALID_TOKEN;
|
return TOKEN_INVALID_TOKEN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BinaryOp binaryop_from_token(TokenType type)
|
||||||
|
{
|
||||||
|
return binary_op[type];
|
||||||
|
}
|
||||||
|
|
||||||
|
TokenType binaryop_to_token(BinaryOp type)
|
||||||
|
{
|
||||||
|
for (unsigned i = 0; i < 256; i++)
|
||||||
|
{
|
||||||
|
if (binary_op[i] == type) return (TokenType)i;
|
||||||
|
}
|
||||||
|
return TOKEN_INVALID_TOKEN;
|
||||||
|
}
|
||||||
|
|
||||||
UnaryOp unaryop_from_token(TokenType type)
|
UnaryOp unaryop_from_token(TokenType type)
|
||||||
{
|
{
|
||||||
return unary_op[type];
|
return unary_op[type];
|
||||||
@@ -183,6 +239,9 @@ void fprint_type_recursive(FILE *file, Type *type, int indent)
|
|||||||
case TYPE_POISONED:
|
case TYPE_POISONED:
|
||||||
fprintf(file, "(POISON)\n");
|
fprintf(file, "(POISON)\n");
|
||||||
return;
|
return;
|
||||||
|
case TYPE_FUNC:
|
||||||
|
fprintf(file, "(FUNC %s)\n", type->func.signature->mangled_signature);
|
||||||
|
break;
|
||||||
case TYPE_USER_DEFINED:
|
case TYPE_USER_DEFINED:
|
||||||
if (type->resolve_status == RESOLVE_DONE)
|
if (type->resolve_status == RESOLVE_DONE)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ bool ptxi(Expr* left, Type *canonical, Type *type, CastType cast_type)
|
|||||||
left->type = type;
|
left->type = type;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
insert_cast(left, CAST_PTRINT, canonical);
|
insert_cast(left, CAST_PTRXI, canonical);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -503,10 +503,10 @@ Type *ARITHMETIC_PROMOTION[19][19] = {
|
|||||||
{ &t_i32, &t_i32, &t_i32, &t_i32, &t_i64, &t_i32, &t_i32, &t_i32, &t_i32, &t_i64, &t_i32, &t_f32, &t_f64, &t_err, &t_err, &t_isz, &t_err, &t_err, &t_err }, // int
|
{ &t_i32, &t_i32, &t_i32, &t_i32, &t_i64, &t_i32, &t_i32, &t_i32, &t_i32, &t_i64, &t_i32, &t_f32, &t_f64, &t_err, &t_err, &t_isz, &t_err, &t_err, &t_err }, // int
|
||||||
{ &t_i64, &t_i64, &t_i64, &t_i64, &t_i64, &t_i64, &t_i64, &t_i64, &t_i64, &t_i64, &t_i64, &t_f32, &t_f64, &t_err, &t_err, &t_isz, &t_err, &t_err, &t_err }, // long
|
{ &t_i64, &t_i64, &t_i64, &t_i64, &t_i64, &t_i64, &t_i64, &t_i64, &t_i64, &t_i64, &t_i64, &t_f32, &t_f64, &t_err, &t_err, &t_isz, &t_err, &t_err, &t_err }, // long
|
||||||
{ &t_u1, &t_i8, &t_i16, &t_i32, &t_i64, &t_ixx, &t_i8, &t_i16, &t_i32, &t_i64, &t_ixx, &t_f32, &t_f64, &t_fxx, &t_err, &t_isz, &t_err, &t_err, &t_err }, // ixx
|
{ &t_u1, &t_i8, &t_i16, &t_i32, &t_i64, &t_ixx, &t_i8, &t_i16, &t_i32, &t_i64, &t_ixx, &t_f32, &t_f64, &t_fxx, &t_err, &t_isz, &t_err, &t_err, &t_err }, // ixx
|
||||||
{ &t_u8, &t_i8, &t_i16, &t_i32, &t_i64, &t_uxx, &t_u8, &t_u16, &t_u32, &t_i64, &t_uxx, &t_f32, &t_f64, &t_err, &t_err, &t_usz, &t_err, &t_err, &t_err }, // byte
|
{ &t_u8, &t_i8, &t_i16, &t_i32, &t_i64, &t_u8, &t_u8, &t_u16, &t_u32, &t_i64, &t_uxx, &t_f32, &t_f64, &t_err, &t_err, &t_usz, &t_err, &t_err, &t_err }, // byte
|
||||||
{ &t_u16, &t_i16, &t_i16, &t_i32, &t_i64, &t_uxx, &t_u16, &t_u16, &t_u32, &t_i64, &t_uxx, &t_f32, &t_f64, &t_err, &t_err, &t_usz, &t_err, &t_err, &t_err }, // ushort
|
{ &t_u16, &t_i16, &t_i16, &t_i32, &t_i64, &t_u16, &t_u16, &t_u16, &t_u32, &t_i64, &t_uxx, &t_f32, &t_f64, &t_err, &t_err, &t_usz, &t_err, &t_err, &t_err }, // ushort
|
||||||
{ &t_u32, &t_i32, &t_i32, &t_i32, &t_i64, &t_uxx, &t_u32, &t_u32, &t_u32, &t_i64, &t_uxx, &t_f32, &t_f64, &t_err, &t_err, &t_usz, &t_err, &t_err, &t_err }, // uint
|
{ &t_u32, &t_i32, &t_i32, &t_i32, &t_i64, &t_u32, &t_u32, &t_u32, &t_u32, &t_i64, &t_uxx, &t_f32, &t_f64, &t_err, &t_err, &t_usz, &t_err, &t_err, &t_err }, // uint
|
||||||
{ &t_u64, &t_i64, &t_i64, &t_i64, &t_i64, &t_uxx, &t_u64, &t_u64, &t_u64, &t_i64, &t_uxx, &t_f32, &t_f64, &t_err, &t_err, &t_usz, &t_err, &t_err, &t_err }, // ulong
|
{ &t_u64, &t_i64, &t_i64, &t_i64, &t_i64, &t_u64, &t_u64, &t_u64, &t_u64, &t_i64, &t_uxx, &t_f32, &t_f64, &t_err, &t_err, &t_usz, &t_err, &t_err, &t_err }, // ulong
|
||||||
{ &t_u1, &t_i8, &t_i16, &t_i32, &t_i64, &t_uxx, &t_u8, &t_u16, &t_u32, &t_u64, &t_uxx, &t_f32, &t_f64, &t_fxx, &t_err, &t_usz, &t_err, &t_err, &t_err }, // uxx
|
{ &t_u1, &t_i8, &t_i16, &t_i32, &t_i64, &t_uxx, &t_u8, &t_u16, &t_u32, &t_u64, &t_uxx, &t_f32, &t_f64, &t_fxx, &t_err, &t_usz, &t_err, &t_err, &t_err }, // uxx
|
||||||
{ &t_f32, &t_f32, &t_f32, &t_f32, &t_f32, &t_f32, &t_f32, &t_f32, &t_f32, &t_f32, &t_f32, &t_f32, &t_f64, &t_f32, &t_err, &t_err, &t_err, &t_err, &t_err }, // float
|
{ &t_f32, &t_f32, &t_f32, &t_f32, &t_f32, &t_f32, &t_f32, &t_f32, &t_f32, &t_f32, &t_f32, &t_f32, &t_f64, &t_f32, &t_err, &t_err, &t_err, &t_err, &t_err }, // float
|
||||||
{ &t_f64, &t_f64, &t_f64, &t_f64, &t_f64, &t_f64, &t_f64, &t_f64, &t_f64, &t_f64, &t_f64, &t_f64, &t_f64, &t_f64, &t_err, &t_err, &t_err, &t_err, &t_err }, // double
|
{ &t_f64, &t_f64, &t_f64, &t_f64, &t_f64, &t_f64, &t_f64, &t_f64, &t_f64, &t_f64, &t_f64, &t_f64, &t_f64, &t_f64, &t_err, &t_err, &t_err, &t_err, &t_err }, // double
|
||||||
|
|||||||
@@ -94,6 +94,9 @@ static void print_typename(FILE *file, Type *type)
|
|||||||
print_typename(file, type->base);
|
print_typename(file, type->base);
|
||||||
fprintf(file, "[%zu]", type->len);
|
fprintf(file, "[%zu]", type->len);
|
||||||
break;
|
break;
|
||||||
|
case TYPE_FUNC:
|
||||||
|
fprintf(file, "%s", type->name_loc.string);
|
||||||
|
break;
|
||||||
case TYPE_EXPRESSION:
|
case TYPE_EXPRESSION:
|
||||||
UNREACHABLE
|
UNREACHABLE
|
||||||
}
|
}
|
||||||
@@ -179,6 +182,9 @@ static inline void codegen_emit_const_expr_raw(Context *context, Expr *expr)
|
|||||||
case TYPE_VOID:
|
case TYPE_VOID:
|
||||||
UNREACHABLE
|
UNREACHABLE
|
||||||
break;
|
break;
|
||||||
|
case TYPE_FUNC:
|
||||||
|
TODO
|
||||||
|
break;
|
||||||
case TYPE_BOOL:
|
case TYPE_BOOL:
|
||||||
assert(expr->const_expr.type == CONST_BOOL);
|
assert(expr->const_expr.type == CONST_BOOL);
|
||||||
PRINTF(expr->const_expr.b ? "true" : "false");
|
PRINTF(expr->const_expr.b ? "true" : "false");
|
||||||
|
|||||||
@@ -51,7 +51,6 @@ void compiler_parse()
|
|||||||
|
|
||||||
void compiler_compile()
|
void compiler_compile()
|
||||||
{
|
{
|
||||||
builtin_setup();
|
|
||||||
Context **contexts = NULL;
|
Context **contexts = NULL;
|
||||||
VECEACH(build_options.files, i)
|
VECEACH(build_options.files, i)
|
||||||
{
|
{
|
||||||
@@ -63,6 +62,7 @@ void compiler_compile()
|
|||||||
vec_add(contexts, context);
|
vec_add(contexts, context);
|
||||||
parse_file(context);
|
parse_file(context);
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
const char *printf = "printf";
|
const char *printf = "printf";
|
||||||
TokenType t_type = TOKEN_IDENT;
|
TokenType t_type = TOKEN_IDENT;
|
||||||
const char *interned = symtab_add(printf, (uint32_t) 6, fnv1a(printf, (uint32_t)6), &t_type);
|
const char *interned = symtab_add(printf, (uint32_t) 6, fnv1a(printf, (uint32_t)6), &t_type);
|
||||||
@@ -75,7 +75,7 @@ void compiler_compile()
|
|||||||
decl->func.function_signature.rtype = type_void;
|
decl->func.function_signature.rtype = type_void;
|
||||||
decl->resolve_status = RESOLVE_DONE;
|
decl->resolve_status = RESOLVE_DONE;
|
||||||
context_register_global_decl(contexts[0], decl);
|
context_register_global_decl(contexts[0], decl);
|
||||||
|
*/
|
||||||
VECEACH(contexts, i)
|
VECEACH(contexts, i)
|
||||||
{
|
{
|
||||||
sema_analysis_pass_conditional_compilation(contexts[i]);
|
sema_analysis_pass_conditional_compilation(contexts[i]);
|
||||||
@@ -88,21 +88,16 @@ void compiler_compile()
|
|||||||
VECEACH(contexts, i)
|
VECEACH(contexts, i)
|
||||||
{
|
{
|
||||||
Context *context = contexts[i];
|
Context *context = contexts[i];
|
||||||
char buffer[255];
|
llvm_codegen(context);
|
||||||
sprintf(buffer, "%s_test.c", context->module_name.string);
|
|
||||||
FILE *f = fopen(buffer,"w");
|
|
||||||
fprintf(f, "#include <stdbool.h>\n#include <stdint.h>\n");
|
|
||||||
context->codegen_output = f;
|
|
||||||
codegen(context);
|
|
||||||
fclose(f);
|
|
||||||
sprintf(buffer, "cc %s_test.c && ./a.out", context->module_name.string);
|
|
||||||
system(buffer);
|
|
||||||
}
|
}
|
||||||
exit(EXIT_SUCCESS);
|
exit(EXIT_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
void compile_file()
|
void compile_file()
|
||||||
{
|
{
|
||||||
|
target_setup();
|
||||||
|
builtin_setup();
|
||||||
|
|
||||||
if (!vec_size(build_options.files)) error_exit("No files to compile.");
|
if (!vec_size(build_options.files)) error_exit("No files to compile.");
|
||||||
switch (build_options.compile_option)
|
switch (build_options.compile_option)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -6,3 +6,4 @@
|
|||||||
|
|
||||||
void compiler_init();
|
void compiler_init();
|
||||||
void compile_file();
|
void compile_file();
|
||||||
|
|
||||||
|
|||||||
@@ -67,12 +67,24 @@ typedef struct
|
|||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
const char *contents;
|
const char *contents;
|
||||||
const char *name;
|
char *name;
|
||||||
|
char *dir_path;
|
||||||
const char *full_path;
|
const char *full_path;
|
||||||
SourceLoc start_id;
|
SourceLoc start_id;
|
||||||
SourceLoc end_id;
|
SourceLoc end_id;
|
||||||
|
SourceLoc *line_start;
|
||||||
|
SourceLoc current_line_start;
|
||||||
|
int last_line_found;
|
||||||
} File;
|
} File;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
File *file;
|
||||||
|
uint32_t line;
|
||||||
|
uint32_t col;
|
||||||
|
SourceLoc loc;
|
||||||
|
const char *start;
|
||||||
|
} SourcePosition;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
@@ -80,6 +92,32 @@ typedef struct
|
|||||||
Token sub_module;
|
Token sub_module;
|
||||||
} Path;
|
} Path;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
unsigned bitsize : 16;
|
||||||
|
unsigned char bytesize;
|
||||||
|
} TypeBuiltin;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
Path *path;
|
||||||
|
} TypeUnresolved;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
Type *base;
|
||||||
|
union
|
||||||
|
{
|
||||||
|
Expr *unresolved_len;
|
||||||
|
size_t len;
|
||||||
|
};
|
||||||
|
} TypeArray;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
struct _FunctionSignature *signature;
|
||||||
|
} TypeFunc;
|
||||||
|
|
||||||
struct _Type
|
struct _Type
|
||||||
{
|
{
|
||||||
TypeKind type_kind : 6;
|
TypeKind type_kind : 6;
|
||||||
@@ -87,21 +125,15 @@ struct _Type
|
|||||||
Type *canonical;
|
Type *canonical;
|
||||||
Token name_loc;
|
Token name_loc;
|
||||||
Type **ptr_like_canonical;
|
Type **ptr_like_canonical;
|
||||||
|
void *backend_type;
|
||||||
|
void *backend_debug_type;
|
||||||
union
|
union
|
||||||
{
|
{
|
||||||
Decl *decl;
|
Decl *decl;
|
||||||
struct
|
TypeBuiltin builtin;
|
||||||
{
|
TypeUnresolved unresolved;
|
||||||
unsigned bitsize : 16;
|
|
||||||
unsigned char bytesize;
|
|
||||||
} builtin;
|
|
||||||
struct
|
|
||||||
{
|
|
||||||
Path *path;
|
|
||||||
} unresolved;
|
|
||||||
Expr *unresolved_type_expr;
|
Expr *unresolved_type_expr;
|
||||||
struct
|
struct {
|
||||||
{
|
|
||||||
Type *base;
|
Type *base;
|
||||||
union
|
union
|
||||||
{
|
{
|
||||||
@@ -109,6 +141,7 @@ struct _Type
|
|||||||
size_t len;
|
size_t len;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
TypeFunc func;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -147,6 +180,8 @@ typedef struct _VarDecl
|
|||||||
VarDeclKind kind : 3;
|
VarDeclKind kind : 3;
|
||||||
Type *type;
|
Type *type;
|
||||||
Expr *init_expr;
|
Expr *init_expr;
|
||||||
|
void *backend_ref;
|
||||||
|
void *backend_debug_ref;
|
||||||
} VarDecl;
|
} VarDecl;
|
||||||
|
|
||||||
|
|
||||||
@@ -177,12 +212,13 @@ typedef struct
|
|||||||
} EnumDecl;
|
} EnumDecl;
|
||||||
|
|
||||||
|
|
||||||
typedef struct
|
typedef struct _FunctionSignature
|
||||||
{
|
{
|
||||||
bool variadic : 1;
|
bool variadic : 1;
|
||||||
Type *rtype;
|
Type *rtype;
|
||||||
Decl** params;
|
Decl** params;
|
||||||
Token *throws;
|
Token *throws;
|
||||||
|
const char *mangled_signature;
|
||||||
} FunctionSignature;
|
} FunctionSignature;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
@@ -197,6 +233,9 @@ typedef struct
|
|||||||
FunctionSignature function_signature;
|
FunctionSignature function_signature;
|
||||||
Ast *body;
|
Ast *body;
|
||||||
FuncAnnotations *annotations;
|
FuncAnnotations *annotations;
|
||||||
|
Decl **locals;
|
||||||
|
Ast **labels;
|
||||||
|
void *backend_value;
|
||||||
} FuncDecl;
|
} FuncDecl;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
@@ -237,6 +276,7 @@ typedef struct _Decl
|
|||||||
DeclKind decl_kind : 6;
|
DeclKind decl_kind : 6;
|
||||||
Visibility visibility : 2;
|
Visibility visibility : 2;
|
||||||
ResolveStatus resolve_status : 2;
|
ResolveStatus resolve_status : 2;
|
||||||
|
bool is_packed : 1;
|
||||||
/* bool is_exported : 1;
|
/* bool is_exported : 1;
|
||||||
bool is_used : 1;
|
bool is_used : 1;
|
||||||
bool is_used_public : 1;
|
bool is_used_public : 1;
|
||||||
@@ -441,6 +481,7 @@ typedef struct
|
|||||||
bool is_used : 1;
|
bool is_used : 1;
|
||||||
struct _Ast *defer;
|
struct _Ast *defer;
|
||||||
struct _Ast *in_defer;
|
struct _Ast *in_defer;
|
||||||
|
void *backend_value;
|
||||||
} AstLabelStmt;
|
} AstLabelStmt;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
@@ -482,6 +523,7 @@ typedef struct
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
Ast *block;
|
Ast *block;
|
||||||
|
void *backend_value;
|
||||||
} AstCaseStmt;
|
} AstCaseStmt;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
@@ -493,6 +535,7 @@ typedef struct
|
|||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
|
Ast **init;
|
||||||
Ast *cond;
|
Ast *cond;
|
||||||
Ast *incr;
|
Ast *incr;
|
||||||
Ast *body;
|
Ast *body;
|
||||||
@@ -645,7 +688,7 @@ typedef struct _Context
|
|||||||
{
|
{
|
||||||
Token module_name;
|
Token module_name;
|
||||||
Token* module_parameters;
|
Token* module_parameters;
|
||||||
File * file;
|
File* file;
|
||||||
Decl** imports;
|
Decl** imports;
|
||||||
Module *module;
|
Module *module;
|
||||||
STable local_symbols;
|
STable local_symbols;
|
||||||
@@ -691,7 +734,7 @@ extern Diagnostics diagnostics;
|
|||||||
extern Token next_tok;
|
extern Token next_tok;
|
||||||
extern Token tok;
|
extern Token tok;
|
||||||
|
|
||||||
extern Type *type_bool, *type_void, *type_string, *type_voidptr, *type_voidref;
|
extern Type *type_bool, *type_void, *type_string, *type_voidptr;
|
||||||
extern Type *type_float, *type_double;
|
extern Type *type_float, *type_double;
|
||||||
extern Type *type_char, *type_short, *type_int, *type_long, *type_isize;
|
extern Type *type_char, *type_short, *type_int, *type_long, *type_isize;
|
||||||
extern Type *type_byte, *type_ushort, *type_uint, *type_ulong, *type_usize;
|
extern Type *type_byte, *type_ushort, *type_uint, *type_ulong, *type_usize;
|
||||||
@@ -775,7 +818,7 @@ bool cast(Expr *expr, Type *to_type, CastType cast_type);
|
|||||||
bool cast_arithmetic(Expr *expr, Expr *other, const char *action);
|
bool cast_arithmetic(Expr *expr, Expr *other, const char *action);
|
||||||
bool cast_to_runtime(Expr *expr);
|
bool cast_to_runtime(Expr *expr);
|
||||||
|
|
||||||
|
void llvm_codegen(Context *context);
|
||||||
void codegen(Context *context);
|
void codegen(Context *context);
|
||||||
|
|
||||||
bool sema_analyse_expr(Context *context, Expr *expr);
|
bool sema_analyse_expr(Context *context, Expr *expr);
|
||||||
@@ -798,6 +841,7 @@ bool context_add_local(Context *context, Decl *decl);
|
|||||||
Decl *decl_new(DeclKind decl_kind, Token name, Visibility visibility);
|
Decl *decl_new(DeclKind decl_kind, Token name, Visibility visibility);
|
||||||
Decl *decl_new_user_defined_type(Token name, DeclKind decl_type, Visibility visibility);
|
Decl *decl_new_user_defined_type(Token name, DeclKind decl_type, Visibility visibility);
|
||||||
Decl *decl_new_var(Token name, Type *type, VarDeclKind kind, Visibility visibility);
|
Decl *decl_new_var(Token name, Type *type, VarDeclKind kind, Visibility visibility);
|
||||||
|
|
||||||
static inline bool decl_ok(Decl *decl) { return decl->decl_kind != DECL_POISONED; }
|
static inline bool decl_ok(Decl *decl) { return decl->decl_kind != DECL_POISONED; }
|
||||||
static inline bool decl_poison(Decl *decl) { decl->decl_kind = DECL_POISONED; decl->resolve_status = RESOLVE_DONE; return false; }
|
static inline bool decl_poison(Decl *decl) { decl->decl_kind = DECL_POISONED; decl->resolve_status = RESOLVE_DONE; return false; }
|
||||||
static inline bool decl_is_struct_type(Decl *decl) { return decl->decl_kind == DECL_UNION || decl->decl_kind == DECL_STRUCT; }
|
static inline bool decl_is_struct_type(Decl *decl) { return decl->decl_kind == DECL_UNION || decl->decl_kind == DECL_STRUCT; }
|
||||||
@@ -880,7 +924,9 @@ void sema_shadow_error(Decl *decl, Decl *old);
|
|||||||
|
|
||||||
File *source_file_load(const char *filename, bool *already_loaded);
|
File *source_file_load(const char *filename, bool *already_loaded);
|
||||||
File *source_file_from_position(SourceLoc loc);
|
File *source_file_from_position(SourceLoc loc);
|
||||||
|
void source_file_append_line_end(File *file, SourceLoc loc);
|
||||||
|
SourcePosition source_file_find_position_in_file(File *file, SourceLoc loc);
|
||||||
|
SourcePosition source_file_find_position(SourceLoc loc);
|
||||||
|
|
||||||
void stable_init(STable *table, uint32_t initial_size);
|
void stable_init(STable *table, uint32_t initial_size);
|
||||||
void *stable_set(STable *table, const char *key, void *value);
|
void *stable_set(STable *table, const char *key, void *value);
|
||||||
@@ -891,6 +937,10 @@ void stable_clear(STable *table);
|
|||||||
void symtab_init(uint32_t max_size);
|
void symtab_init(uint32_t max_size);
|
||||||
const char *symtab_add(const char *symbol, uint32_t len, uint32_t fnv1hash, TokenType *type);
|
const char *symtab_add(const char *symbol, uint32_t len, uint32_t fnv1hash, TokenType *type);
|
||||||
|
|
||||||
|
void target_setup();
|
||||||
|
int target_alloca_addr_space();
|
||||||
|
void *target_data_layout();
|
||||||
|
|
||||||
#define TOKEN_MAX_LENGTH 0xFFFF
|
#define TOKEN_MAX_LENGTH 0xFFFF
|
||||||
#define TOK2VARSTR(_token) _token.span.length, _token.start
|
#define TOK2VARSTR(_token) _token.span.length, _token.start
|
||||||
bool token_is_type(TokenType type);
|
bool token_is_type(TokenType type);
|
||||||
@@ -908,6 +958,8 @@ Type *type_unsigned_int_by_size(int bitsize);
|
|||||||
bool type_is_subtype(Type *type, Type *possible_subtype);
|
bool type_is_subtype(Type *type, Type *possible_subtype);
|
||||||
const char *type_to_error_string(Type *type);
|
const char *type_to_error_string(Type *type);
|
||||||
size_t type_size(Type *canonical);
|
size_t type_size(Type *canonical);
|
||||||
|
void type_append_signature_name(Type *type, char *dst, size_t *offset);
|
||||||
|
|
||||||
static inline bool type_is_builtin(TypeKind kind) { return kind >= TYPE_VOID && kind <= TYPE_FXX; }
|
static inline bool type_is_builtin(TypeKind kind) { return kind >= TYPE_VOID && kind <= TYPE_FXX; }
|
||||||
static inline bool type_is_signed(Type *type) { return type->type_kind >= TYPE_I8 && type->type_kind <= TYPE_IXX; }
|
static inline bool type_is_signed(Type *type) { return type->type_kind >= TYPE_I8 && type->type_kind <= TYPE_IXX; }
|
||||||
static inline bool type_is_unsigned(Type *type) { return type->type_kind >= TYPE_U8 && type->type_kind <= TYPE_UXX; }
|
static inline bool type_is_unsigned(Type *type) { return type->type_kind >= TYPE_U8 && type->type_kind <= TYPE_UXX; }
|
||||||
@@ -920,6 +972,19 @@ static inline bool type_is_integer(Type *type)
|
|||||||
return type->type_kind >= TYPE_I8 && type->type_kind <= TYPE_UXX;
|
return type->type_kind >= TYPE_I8 && type->type_kind <= TYPE_UXX;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool type_is_float(Type *type)
|
||||||
|
{
|
||||||
|
assert(type == type->canonical);
|
||||||
|
return type->type_kind >= TYPE_F32 && type->type_kind <= TYPE_FXX;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool type_convert_will_trunc(Type *destination, Type *source)
|
||||||
|
{
|
||||||
|
assert(type_is_builtin(destination->canonical->type_kind));
|
||||||
|
assert(type_is_builtin(source->canonical->type_kind));
|
||||||
|
return (unsigned)destination->canonical->builtin.bitsize < (unsigned)source->canonical->builtin.bitsize;
|
||||||
|
}
|
||||||
|
|
||||||
static inline bool type_is_number(Type *type)
|
static inline bool type_is_number(Type *type)
|
||||||
{
|
{
|
||||||
assert(type == type->canonical);
|
assert(type == type->canonical);
|
||||||
@@ -932,6 +997,10 @@ static inline bool type_is_number(Type *type)
|
|||||||
|
|
||||||
AssignOp assignop_from_token(TokenType type);
|
AssignOp assignop_from_token(TokenType type);
|
||||||
UnaryOp unaryop_from_token(TokenType type);
|
UnaryOp unaryop_from_token(TokenType type);
|
||||||
|
BinaryOp binaryop_from_token(TokenType type);
|
||||||
|
BinaryOp binaryop_assign_base_op(BinaryOp assign_binary_op);
|
||||||
|
|
||||||
|
|
||||||
Decl *struct_find_name(Decl *decl, const char* name);
|
Decl *struct_find_name(Decl *decl, const char* name);
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -28,68 +28,44 @@ typedef enum
|
|||||||
|
|
||||||
static void print_error(SourceRange source_range, const char *message, PrintType print_type)
|
static void print_error(SourceRange source_range, const char *message, PrintType print_type)
|
||||||
{
|
{
|
||||||
File *file = source_file_from_position(source_range.loc);
|
SourcePosition position = source_file_find_position(source_range.loc);
|
||||||
|
|
||||||
const char *content = file->contents;
|
|
||||||
const char *error_start = file->contents + source_range.loc - file->start_id;
|
|
||||||
|
|
||||||
const static int LINES_SHOWN = 4;
|
const static int LINES_SHOWN = 4;
|
||||||
|
|
||||||
const char *linestarts[LINES_SHOWN];
|
unsigned max_line_length = (int)round(log10(position.line)) + 1;
|
||||||
for (int i = 0; i < LINES_SHOWN; i++) linestarts[i] = NULL;
|
|
||||||
const char *current = content;
|
|
||||||
linestarts[0] = content;
|
|
||||||
unsigned line = 1;
|
|
||||||
while (current < error_start)
|
|
||||||
{
|
|
||||||
if (current[0] == '\n')
|
|
||||||
{
|
|
||||||
line++;
|
|
||||||
linestarts[3] = linestarts[2];
|
|
||||||
linestarts[2] = linestarts[1];
|
|
||||||
linestarts[1] = linestarts[0];
|
|
||||||
linestarts[0] = current + 1;
|
|
||||||
}
|
|
||||||
current++;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *end = NULL;
|
|
||||||
while (!end)
|
|
||||||
{
|
|
||||||
switch (current[0])
|
|
||||||
{
|
|
||||||
case '\n':
|
|
||||||
case '\0':
|
|
||||||
end = current;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
current++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned max_line_length = (int)round(log10(line)) + 1;
|
|
||||||
|
|
||||||
char number_buffer[20];
|
char number_buffer[20];
|
||||||
snprintf(number_buffer, 20, "%%%dd: %%.*s\n", max_line_length);
|
snprintf(number_buffer, 20, "%%%dd: %%.*s\n", max_line_length);
|
||||||
|
|
||||||
for (unsigned i = 3; i > 0; i--)
|
// Insert end in case it's not yet there.
|
||||||
|
for (SourceLoc s = position.loc; s < position.file->end_id; s++)
|
||||||
{
|
{
|
||||||
int line_number = (int)line - i;
|
if ((position.file->contents + s - position.file->start_id)[0] == '\n')
|
||||||
const char *start = linestarts[i];
|
{
|
||||||
if (start == NULL) continue;
|
source_file_append_line_end(position.file, s);
|
||||||
const char *line_end = linestarts[i - 1];
|
break;
|
||||||
eprintf(number_buffer, line_number, line_end - start - 1, start);
|
}
|
||||||
|
}
|
||||||
|
size_t lines_in_file = vec_size(position.file->line_start);
|
||||||
|
for (unsigned i = LINES_SHOWN; i > 0; i--)
|
||||||
|
{
|
||||||
|
if (position.line < i) continue;
|
||||||
|
uint32_t line_number = position.line + 1 - i;
|
||||||
|
SourceLoc line_start = position.file->line_start[line_number - 1];
|
||||||
|
|
||||||
|
SourceLoc line_end = line_number == lines_in_file ? position.file->end_id :
|
||||||
|
position.file->line_start[line_number];
|
||||||
|
uint32_t line_len = line_end - line_start - 1;
|
||||||
|
eprintf(number_buffer, line_number, line_len, position.file->contents + line_start - position.file->start_id);
|
||||||
}
|
}
|
||||||
eprintf(number_buffer, line, end - linestarts[0], linestarts[0]);
|
|
||||||
eprintf(" ");
|
eprintf(" ");
|
||||||
for (unsigned i = 0; i < max_line_length; i++)
|
for (unsigned i = 0; i < max_line_length; i++)
|
||||||
{
|
{
|
||||||
eprintf(" ");
|
eprintf(" ");
|
||||||
}
|
}
|
||||||
for (unsigned i = 0; i < error_start - linestarts[0]; i++)
|
for (unsigned i = 0; i < position.col - 1; i++)
|
||||||
{
|
{
|
||||||
if (linestarts[0][i] == '\t')
|
if (position.start[i] == '\t')
|
||||||
{
|
{
|
||||||
eprintf("\t");
|
eprintf("\t");
|
||||||
}
|
}
|
||||||
@@ -107,13 +83,13 @@ static void print_error(SourceRange source_range, const char *message, PrintType
|
|||||||
switch (print_type)
|
switch (print_type)
|
||||||
{
|
{
|
||||||
case PRINT_TYPE_ERROR:
|
case PRINT_TYPE_ERROR:
|
||||||
eprintf("(%s:%d) Error: %s\n", file->name, line, message);
|
eprintf("(%s:%d) Error: %s\n", position.file->name, position.line, message);
|
||||||
break;
|
break;
|
||||||
case PRINT_TYPE_PREV:
|
case PRINT_TYPE_PREV:
|
||||||
eprintf("(%s:%d) %s\n", file->name, line, message);
|
eprintf("(%s:%d) %s\n", position.file->name, position.line, message);
|
||||||
break;
|
break;
|
||||||
case PRINT_TYPE_WARN:
|
case PRINT_TYPE_WARN:
|
||||||
eprintf("(%s:%d) Warning: %s\n", file->name, line, message);
|
eprintf("(%s:%d) Warning: %s\n", position.file->name, position.line, message);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
UNREACHABLE
|
UNREACHABLE
|
||||||
|
|||||||
284
src/compiler/dwarf.h
Normal file
284
src/compiler/dwarf.h
Normal file
@@ -0,0 +1,284 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
#define DW_TAG_array_type 0x01
|
||||||
|
#define DW_TAG_class_type 0x02
|
||||||
|
#define DW_TAG_entry_point 0x03
|
||||||
|
#define DW_TAG_enumeration_type 0x04
|
||||||
|
#define DW_TAG_formal_parameter 0x05
|
||||||
|
#define DW_TAG_imported_declaration 0x08
|
||||||
|
#define DW_TAG_label 0x0a
|
||||||
|
#define DW_TAG_lexical_block 0x0b
|
||||||
|
#define DW_TAG_member 0x0d
|
||||||
|
#define DW_TAG_pointer_type 0x0f
|
||||||
|
#define DW_TAG_reference_type 0x10
|
||||||
|
#define DW_TAG_compile_unit 0x11
|
||||||
|
#define DW_TAG_string_type 0x12
|
||||||
|
#define DW_TAG_structure_type 0x13
|
||||||
|
#define DW_TAG_subroutine_type 0x15
|
||||||
|
#define DW_TAG_typedef 0x16
|
||||||
|
#define DW_TAG_union_type 0x17
|
||||||
|
#define DW_TAG_unspecified_parameters 0x18
|
||||||
|
#define DW_TAG_variant 0x19
|
||||||
|
#define DW_TAG_common_block 0x1a
|
||||||
|
#define DW_TAG_common_inclusion 0x1b
|
||||||
|
#define DW_TAG_inheritance 0x1c
|
||||||
|
#define DW_TAG_inlined_subroutine 01x1d
|
||||||
|
#define DW_TAG_module 0x1e
|
||||||
|
#define DW_TAG_ptr_to_member_type 0x1f
|
||||||
|
#define DW_TAG_set_type 0x20
|
||||||
|
#define DW_TAG_subrange_type 0x21
|
||||||
|
#define DW_TAG_with_stmt 0x22
|
||||||
|
#define DW_TAG_access_declaration 0x23
|
||||||
|
#define DW_TAG_base_type 0x24
|
||||||
|
#define DW_TAG_catch_block 0x25
|
||||||
|
#define DW_TAG_const_type 0x26
|
||||||
|
#define DW_TAG_constant 0x27
|
||||||
|
#define DW_TAG_enumerator 0x28
|
||||||
|
#define DW_TAG_file_type 0x29
|
||||||
|
#define DW_TAG_friend 0x2a
|
||||||
|
#define DW_TAG_namelist 0x2b
|
||||||
|
#define DW_TAG_namelist_item 0x2c
|
||||||
|
#define DW_TAG_packed_type 0x2d
|
||||||
|
#define DW_TAG_subprogram 0x2e
|
||||||
|
#define DW_TAG_template_type_param 0x2f
|
||||||
|
#define DW_TAG_template_value_param 0x30
|
||||||
|
#define DW_TAG_thrown_type 0x31
|
||||||
|
#define DW_TAG_try_block 0x32
|
||||||
|
#define DW_TAG_variant_part 0x33
|
||||||
|
#define DW_TAG_variable 0x34
|
||||||
|
#define DW_TAG_volatile_type 0x35
|
||||||
|
|
||||||
|
#define DW_CHILDREN_no 0
|
||||||
|
#define DW_CHILDREN_yes 1
|
||||||
|
|
||||||
|
#define DW_LANG_C89 0x0001
|
||||||
|
|
||||||
|
#define DW_AT_sibling 0x01
|
||||||
|
#define DW_AT_location 0x02
|
||||||
|
#define DW_AT_name 0x03
|
||||||
|
#define DW_AT_ordering 0x09
|
||||||
|
#define DW_AT_byte_size 0x0b
|
||||||
|
#define DW_AT_bit_offset 0x0c
|
||||||
|
#define DW_AT_bit_size 0x0d
|
||||||
|
#define DW_AT_stmt_list 0x10
|
||||||
|
#define DW_AT_low_pc 0x11
|
||||||
|
#define DW_AT_high_pc 0x12
|
||||||
|
#define DW_AT_language 0x13
|
||||||
|
#define DW_AT_discr 0x15
|
||||||
|
#define DW_AT_discr_value 0x16
|
||||||
|
#define DW_AT_visibility 0x17
|
||||||
|
#define DW_AT_import 0x18
|
||||||
|
#define DW_AT_string_length 0x19
|
||||||
|
#define DW_AT_common_reference 0x1a
|
||||||
|
#define DW_AT_comp_dir 0x1b
|
||||||
|
#define DW_AT_const_value 0x1c
|
||||||
|
#define DW_AT_containing_type 0x1d
|
||||||
|
#define DW_AT_default_value 0x1e
|
||||||
|
#define DW_AT_inline 0x20
|
||||||
|
#define DW_AT_is_optional 0x21
|
||||||
|
#define DW_AT_lower_bound 0x22
|
||||||
|
#define DW_AT_producer 0x25
|
||||||
|
#define DW_AT_prototyped 0x27
|
||||||
|
#define DW_AT_return_addr 0x2a
|
||||||
|
#define DW_AT_start_scope 0x2c
|
||||||
|
#define DW_AT_stride_size 0x2e
|
||||||
|
#define DW_AT_upper_bound 0x2f
|
||||||
|
#define DW_AT_abstract_origin 0x31
|
||||||
|
#define DW_AT_accessibility 0x32
|
||||||
|
#define DW_AT_address_class 0x33
|
||||||
|
#define DW_AT_artificial 0x34
|
||||||
|
#define DW_AT_base_types 0x35
|
||||||
|
#define DW_AT_calling_convention 0x36
|
||||||
|
#define DW_AT_count 0x37
|
||||||
|
#define DW_AT_data_member_location 0x38
|
||||||
|
#define DW_AT_decl_column 0x39
|
||||||
|
#define DW_AT_decl_file 0x3a
|
||||||
|
#define DW_AT_decl_line 0x3b
|
||||||
|
#define DW_AT_declaration 0x3c
|
||||||
|
#define DW_AT_discr_list 0x3d
|
||||||
|
#define DW_AT_encoding 0x3e
|
||||||
|
#define DW_AT_external 0x3f
|
||||||
|
#define DW_AT_frame_base 0x40
|
||||||
|
#define DW_AT_friend 0x41
|
||||||
|
#define DW_AT_identifier_case 0x42
|
||||||
|
#define DW_AT_macro_info 0x43
|
||||||
|
#define DW_AT_namelist_item 0x44
|
||||||
|
#define DW_AT_priority 0x45
|
||||||
|
#define DW_AT_segment 0x46
|
||||||
|
#define DW_AT_specification 0x47
|
||||||
|
#define DW_AT_static_link 0x48
|
||||||
|
#define DW_AT_type 0x49
|
||||||
|
#define DW_AT_use_location 0x4a
|
||||||
|
#define DW_AT_variable_parameter 0x4b
|
||||||
|
#define DW_AT_virtuality 0x4c
|
||||||
|
#define DW_AT_vtable_elem_location 0x4d
|
||||||
|
#define DW_AT_lo_user 0x2000
|
||||||
|
#define DW_AT_hi_user 0x3fff
|
||||||
|
|
||||||
|
#define DW_AT_user_block 0x2650
|
||||||
|
#define DW_AT_user_level 0x2651
|
||||||
|
|
||||||
|
#define DW_FORM_addr 0x01
|
||||||
|
#define DW_FORM_block2 0x03
|
||||||
|
#define DW_FORM_block4 0x04
|
||||||
|
#define DW_FORM_data2 0x05
|
||||||
|
#define DW_FORM_data4 0x06
|
||||||
|
#define DW_FORM_data8 0x07
|
||||||
|
#define DW_FORM_string 0x08
|
||||||
|
#define DW_FORM_block 0x09
|
||||||
|
#define DW_FORM_block1 0x0a
|
||||||
|
#define DW_FORM_data1 0x0b
|
||||||
|
#define DW_FORM_flag 0x0c
|
||||||
|
#define DW_FORM_sdata 0x0d
|
||||||
|
#define DW_FORM_strp 0x0e
|
||||||
|
#define DW_FORM_udata 0x0f
|
||||||
|
#define DW_FORM_ref_addr 0x10
|
||||||
|
#define DW_FORM_ref1 0x11
|
||||||
|
#define DW_FORM_ref2 0x12
|
||||||
|
#define DW_FORM_ref4 0x13
|
||||||
|
#define DW_FORM_ref_udata 0x15
|
||||||
|
#define DW_FORM_indirect 0x16
|
||||||
|
|
||||||
|
#define DW_OP_addr 0x03
|
||||||
|
#define DW_OP_deref 0x06
|
||||||
|
#define DW_OP_const1u 0x08
|
||||||
|
#define DW_OP_const1s 0x09
|
||||||
|
#define DW_OP_const2u 0x0a
|
||||||
|
#define DW_OP_const2s 0x0b
|
||||||
|
#define DW_OP_const4u 0x0c
|
||||||
|
#define DW_OP_const4s 0x0d
|
||||||
|
#define DW_OP_const8u 0x0e
|
||||||
|
#define DW_OP_const8s 0x0f
|
||||||
|
#define DW_OP_constu 0x10
|
||||||
|
#define DW_OP_consts 0x11
|
||||||
|
#define DW_OP_dup 0x12
|
||||||
|
#define DW_OP_drop 0x13
|
||||||
|
#define DW_OP_over 0x14
|
||||||
|
#define DW_OP_pick 0x15
|
||||||
|
#define DW_OP_swap 0x16
|
||||||
|
#define DW_OP_rot 0x17
|
||||||
|
#define DW_OP_xderef 0x18
|
||||||
|
#define DW_OP_abs 0x19
|
||||||
|
#define DW_OP_and 0x1a
|
||||||
|
#define DW_OP_div 0x1b
|
||||||
|
#define DW_OP_minus 0x1c
|
||||||
|
#define DW_OP_mod 0x1d
|
||||||
|
#define DW_OP_mul 0x1e
|
||||||
|
#define DW_OP_neg 0x1f
|
||||||
|
#define DW_OP_not 0x20
|
||||||
|
#define DW_OP_or 0x21
|
||||||
|
#define DW_OP_plus 0x22
|
||||||
|
#define DW_OP_plus_uconst 0x23
|
||||||
|
#define DW_OP_shl 0x24
|
||||||
|
#define DW_OP_shr 0x25
|
||||||
|
#define DW_OP_shra 0x26
|
||||||
|
#define DW_OP_xor 0x27
|
||||||
|
#define DW_OP_skip 0x2f
|
||||||
|
#define DW_OP_bra 0x28
|
||||||
|
#define DW_OP_eq 0x29
|
||||||
|
#define DW_OP_ge 0x2a
|
||||||
|
#define DW_OP_gt 0x2b
|
||||||
|
#define DW_OP_le 0x2c
|
||||||
|
#define DW_OP_lt 0x2d
|
||||||
|
#define DW_OP_ne 0x2e
|
||||||
|
#define DW_OP_lit0 0x30
|
||||||
|
#define DW_OP_reg0 0x50
|
||||||
|
#define DW_OP_breg0 0x70
|
||||||
|
#define DW_OP_breg1 0x71
|
||||||
|
#define DW_OP_breg2 0x72
|
||||||
|
#define DW_OP_breg3 0x73
|
||||||
|
#define DW_OP_breg4 0x74
|
||||||
|
#define DW_OP_breg5 0x75
|
||||||
|
#define DW_OP_breg6 0x76
|
||||||
|
#define DW_OP_breg7 0x77
|
||||||
|
#define DW_OP_breg8 0x78
|
||||||
|
#define DW_OP_breg9 0x79
|
||||||
|
#define DW_OP_breg10 0x7a
|
||||||
|
#define DW_OP_breg11 0x7b
|
||||||
|
#define DW_OP_breg12 0x7c
|
||||||
|
#define DW_OP_breg13 0x7d
|
||||||
|
#define DW_OP_breg14 0x7e
|
||||||
|
#define DW_OP_breg15 0x7f
|
||||||
|
#define DW_OP_breg16 0x80
|
||||||
|
#define DW_OP_breg17 0x81
|
||||||
|
#define DW_OP_breg18 0x82
|
||||||
|
#define DW_OP_breg19 0x83
|
||||||
|
#define DW_OP_breg20 0x84
|
||||||
|
#define DW_OP_breg21 0x85
|
||||||
|
#define DW_OP_breg22 0x86
|
||||||
|
#define DW_OP_breg23 0x87
|
||||||
|
#define DW_OP_breg24 0x88
|
||||||
|
#define DW_OP_breg25 0x89
|
||||||
|
#define DW_OP_breg26 0x8a
|
||||||
|
#define DW_OP_breg27 0x8b
|
||||||
|
#define DW_OP_breg28 0x8c
|
||||||
|
#define DW_OP_breg29 0x8d
|
||||||
|
#define DW_OP_breg30 0x8e
|
||||||
|
#define DW_OP_breg31 0x8f
|
||||||
|
#define DW_OP_regx 0x90
|
||||||
|
#define DW_OP_fbreg 0x91
|
||||||
|
#define DW_OP_bregx 0x92
|
||||||
|
#define DW_OP_piece 0x93
|
||||||
|
#define DW_OP_deref_size 0x94
|
||||||
|
#define DW_OP_xderef_size 0x95
|
||||||
|
#define DW_OP_nop 0x96
|
||||||
|
|
||||||
|
#define DW_ATE_address 0x1
|
||||||
|
#define DW_ATE_boolean 0x2
|
||||||
|
#define DW_ATE_complex_float 0x3
|
||||||
|
#define DW_ATE_float 0x4
|
||||||
|
#define DW_ATE_signed 0x5
|
||||||
|
#define DW_ATE_signed_char 0x6
|
||||||
|
#define DW_ATE_unsigned 0x7
|
||||||
|
#define DW_ATE_unsigned_char 0x8
|
||||||
|
|
||||||
|
#define DW_CC_normal 0x1
|
||||||
|
#define DW_CC_program 0x2
|
||||||
|
#define DW_CC_nocall 0x3
|
||||||
|
|
||||||
|
#define DW_LNS_copy 1
|
||||||
|
#define DW_LNS_advance_pc 2
|
||||||
|
#define DW_LNS_advance_line 3
|
||||||
|
#define DW_LNS_set_file 4
|
||||||
|
#define DW_LNS_set_column 5
|
||||||
|
#define DW_LNS_negate_stmt 6
|
||||||
|
#define DW_LNS_set_basic_block 7
|
||||||
|
#define DW_LNS_const_add_pc 8
|
||||||
|
#define DW_LNS_fixed_advance_pc 9
|
||||||
|
|
||||||
|
#define DW_LNE_end_sequence 1
|
||||||
|
#define DW_LNE_set_address 2
|
||||||
|
#define DW_LNE_define_file 3
|
||||||
|
|
||||||
|
#define DW_MACINFO_define 1
|
||||||
|
#define DW_MACINFO_undef 2
|
||||||
|
#define DW_MACINFO_start_file 3
|
||||||
|
#define DW_MACINFO_end_file 4
|
||||||
|
#define DW_MACINFO_vendor_ext 255
|
||||||
|
|
||||||
|
#define DW_CFA_advance_loc (1 << 6)
|
||||||
|
#define DW_CFA_offset (2 << 6)
|
||||||
|
#define DW_CFA_restore (3 << 6)
|
||||||
|
#define DW_CFA_set_loc 0x01
|
||||||
|
#define DW_CFA_advance_loc1 0x02
|
||||||
|
#define DW_CFA_advance_loc2 0x03
|
||||||
|
#define DW_CFA_advance_loc4 0x04
|
||||||
|
#define DW_CFA_offset_extended 0x05
|
||||||
|
#define DW_CFA_restore_extended 0x06
|
||||||
|
#define DW_CFA_undefined 0x07
|
||||||
|
#define DW_CFA_same_value 0x08
|
||||||
|
#define DW_CFA_register 0x09
|
||||||
|
#define DW_CFA_remember_state 0x0a
|
||||||
|
#define DW_CFA_restore_state 0x0b
|
||||||
|
#define DW_CFA_def_cfa 0x0c
|
||||||
|
#define DW_CFA_def_cfa_register 0x0d
|
||||||
|
#define DW_CFA_def_cfa_offset 0x0e
|
||||||
|
#define DW_CFA_nop 0x0f
|
||||||
|
#define DW_CFA_lo_user 0x1c
|
||||||
|
#define DW_CFA_hi_user 0x3f
|
||||||
|
|
||||||
|
#define DWARF_PRODUCER_NAME "C3 Compiler"
|
||||||
@@ -22,6 +22,43 @@ typedef enum
|
|||||||
ASSIGNOP_SHL_ASSIGN,
|
ASSIGNOP_SHL_ASSIGN,
|
||||||
} AssignOp;
|
} AssignOp;
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
BINARYOP_ERROR,
|
||||||
|
BINARYOP_MULT,
|
||||||
|
BINARYOP_SUB,
|
||||||
|
BINARYOP_ADD,
|
||||||
|
BINARYOP_DIV,
|
||||||
|
BINARYOP_MOD,
|
||||||
|
BINARYOP_SHR,
|
||||||
|
BINARYOP_SHL,
|
||||||
|
BINARYOP_BIT_OR,
|
||||||
|
BINARYOP_BIT_XOR,
|
||||||
|
BINARYOP_BIT_AND,
|
||||||
|
BINARYOP_AND,
|
||||||
|
BINARYOP_OR,
|
||||||
|
BINARYOP_GT,
|
||||||
|
BINARYOP_GE,
|
||||||
|
BINARYOP_LT,
|
||||||
|
BINARYOP_LE,
|
||||||
|
BINARYOP_NE,
|
||||||
|
BINARYOP_EQ,
|
||||||
|
// Only "assign" BINOPS after this point
|
||||||
|
BINARYOP_ASSIGN,
|
||||||
|
BINARYOP_MULT_ASSIGN,
|
||||||
|
BINARYOP_ADD_ASSIGN,
|
||||||
|
BINARYOP_SUB_ASSIGN,
|
||||||
|
BINARYOP_DIV_ASSIGN,
|
||||||
|
BINARYOP_MOD_ASSIGN,
|
||||||
|
BINARYOP_AND_ASSIGN,
|
||||||
|
BINARYOP_OR_ASSIGN,
|
||||||
|
BINARYOP_BIT_AND_ASSIGN,
|
||||||
|
BINARYOP_BIT_OR_ASSIGN,
|
||||||
|
BINARYOP_BIT_XOR_ASSIGN,
|
||||||
|
BINARYOP_SHR_ASSIGN,
|
||||||
|
BINARYOP_SHL_ASSIGN,
|
||||||
|
} BinaryOp;
|
||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
AST_POISONED,
|
AST_POISONED,
|
||||||
@@ -78,15 +115,15 @@ typedef enum
|
|||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
CAST_ERROR,
|
CAST_ERROR,
|
||||||
CAST_TRUNC,
|
|
||||||
CAST_PTRPTR,
|
CAST_PTRPTR,
|
||||||
CAST_PTRINT,
|
CAST_PTRXI,
|
||||||
CAST_VARRPTR,
|
CAST_VARRPTR,
|
||||||
CAST_ARRPTR,
|
CAST_ARRPTR,
|
||||||
CAST_STRPTR,
|
CAST_STRPTR,
|
||||||
CAST_PTRBOOL,
|
CAST_PTRBOOL,
|
||||||
CAST_BOOLINT,
|
CAST_BOOLINT,
|
||||||
CAST_BOOLFP,
|
CAST_BOOLFP,
|
||||||
|
CAST_FPBOOL,
|
||||||
CAST_INTBOOL,
|
CAST_INTBOOL,
|
||||||
CAST_FPFP,
|
CAST_FPFP,
|
||||||
CAST_FPSI,
|
CAST_FPSI,
|
||||||
@@ -219,7 +256,6 @@ typedef enum
|
|||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
LEXER_STATE_NORMAL,
|
LEXER_STATE_NORMAL,
|
||||||
LEXER_STATE_DEFERED_PARSING,
|
|
||||||
LEXER_STATE_DOCS_PARSE,
|
LEXER_STATE_DOCS_PARSE,
|
||||||
LEXER_STATE_DOCS_PARSE_DIRECTIVE,
|
LEXER_STATE_DOCS_PARSE_DIRECTIVE,
|
||||||
} LexerState;
|
} LexerState;
|
||||||
@@ -467,6 +503,7 @@ typedef enum
|
|||||||
TYPE_F32,
|
TYPE_F32,
|
||||||
TYPE_F64,
|
TYPE_F64,
|
||||||
TYPE_FXX,
|
TYPE_FXX,
|
||||||
|
TYPE_FUNC,
|
||||||
TYPE_USER_DEFINED,
|
TYPE_USER_DEFINED,
|
||||||
TYPE_POINTER,
|
TYPE_POINTER,
|
||||||
TYPE_STRING,
|
TYPE_STRING,
|
||||||
|
|||||||
@@ -726,6 +726,7 @@ static bool sema_expr_analyse_shl_assign(Context *context, Expr *expr, Expr *lef
|
|||||||
|
|
||||||
static bool sema_expr_analyse_and(Context *context, Expr *expr, Expr *left, Expr *right)
|
static bool sema_expr_analyse_and(Context *context, Expr *expr, Expr *left, Expr *right)
|
||||||
{
|
{
|
||||||
|
expr->type = type_bool;
|
||||||
if (!cast(left, type_bool, CAST_TYPE_IMPLICIT)) return false;
|
if (!cast(left, type_bool, CAST_TYPE_IMPLICIT)) return false;
|
||||||
if (!cast(right, type_bool, CAST_TYPE_IMPLICIT)) return false;
|
if (!cast(right, type_bool, CAST_TYPE_IMPLICIT)) return false;
|
||||||
if (both_const(left, right))
|
if (both_const(left, right))
|
||||||
@@ -905,6 +906,7 @@ static bool sema_expr_analyse_not(Context *context, Expr *expr, Expr *inner)
|
|||||||
case TYPE_INC_ARRAY:
|
case TYPE_INC_ARRAY:
|
||||||
case TYPE_EXPRESSION:
|
case TYPE_EXPRESSION:
|
||||||
UNREACHABLE
|
UNREACHABLE
|
||||||
|
case TYPE_FUNC:
|
||||||
case TYPE_ARRAY:
|
case TYPE_ARRAY:
|
||||||
case TYPE_POINTER:
|
case TYPE_POINTER:
|
||||||
case TYPE_VARARRAY:
|
case TYPE_VARARRAY:
|
||||||
|
|||||||
@@ -46,6 +46,11 @@ static inline void backtrack()
|
|||||||
lexer.current--;
|
lexer.current--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void lexer_store_line_end(void)
|
||||||
|
{
|
||||||
|
source_file_append_line_end(lexer.current_file, lexer.current_file->start_id + lexer.current - lexer.file_begin);
|
||||||
|
}
|
||||||
|
|
||||||
void lexer_store_state(void)
|
void lexer_store_state(void)
|
||||||
{
|
{
|
||||||
lexer.stored.current = lexer.current;
|
lexer.stored.current = lexer.current;
|
||||||
@@ -102,7 +107,7 @@ static Token error_token(const char *message, ...)
|
|||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Token make_token(TokenType type)
|
static Token make_token(TokenType type, const char *string)
|
||||||
{
|
{
|
||||||
size_t token_size = lexer.current - lexer.lexing_start;
|
size_t token_size = lexer.current - lexer.lexing_start;
|
||||||
if (token_size > TOKEN_MAX_LENGTH) return error_token("Token exceeding max length");
|
if (token_size > TOKEN_MAX_LENGTH) return error_token("Token exceeding max length");
|
||||||
@@ -110,7 +115,7 @@ static Token make_token(TokenType type)
|
|||||||
{
|
{
|
||||||
.type = type,
|
.type = type,
|
||||||
.span = { .loc = (SourceLoc) (lexer.current_file->start_id + (lexer.lexing_start - lexer.file_begin)), .length = token_size },
|
.span = { .loc = (SourceLoc) (lexer.current_file->start_id + (lexer.lexing_start - lexer.file_begin)), .length = token_size },
|
||||||
.start = lexer.lexing_start
|
.start = string
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,6 +172,7 @@ SkipWhitespaceResult skip_whitespace()
|
|||||||
case '\0':
|
case '\0':
|
||||||
return WHITESPACE_FOUND_EOF;
|
return WHITESPACE_FOUND_EOF;
|
||||||
case '\n':
|
case '\n':
|
||||||
|
lexer_store_line_end();
|
||||||
// If we are currently parsing docs, then end of line is considered meaningful.
|
// If we are currently parsing docs, then end of line is considered meaningful.
|
||||||
if (lexer.lexer_state == LEXER_STATE_DOCS_PARSE_DIRECTIVE) return WHITESPACE_FOUND_DOCS_EOL;
|
if (lexer.lexer_state == LEXER_STATE_DOCS_PARSE_DIRECTIVE) return WHITESPACE_FOUND_DOCS_EOL;
|
||||||
case ' ':
|
case ' ':
|
||||||
@@ -231,7 +237,7 @@ SkipWhitespaceResult skip_whitespace()
|
|||||||
|
|
||||||
// --- Normal scanning methods start here
|
// --- Normal scanning methods start here
|
||||||
|
|
||||||
static inline Token scan_prefixed_ident(TokenType type, TokenType no_ident_type, bool ends_with_bang)
|
static inline Token scan_prefixed_ident(TokenType type, TokenType no_ident_type, bool ends_with_bang, const char *start)
|
||||||
{
|
{
|
||||||
uint32_t hash = FNV1a(prev(), FNV1_SEED);
|
uint32_t hash = FNV1a(prev(), FNV1_SEED);
|
||||||
while (is_alphanum_(peek()))
|
while (is_alphanum_(peek()))
|
||||||
@@ -243,7 +249,7 @@ static inline Token scan_prefixed_ident(TokenType type, TokenType no_ident_type,
|
|||||||
hash = FNV1a(next(), hash);
|
hash = FNV1a(next(), hash);
|
||||||
}
|
}
|
||||||
uint32_t len = (uint32_t)(lexer.current - lexer.lexing_start);
|
uint32_t len = (uint32_t)(lexer.current - lexer.lexing_start);
|
||||||
if (len == 1) return make_token(no_ident_type);
|
if (len == 1) return make_token(no_ident_type, start);
|
||||||
const char* interned = symtab_add(lexer.lexing_start, len, hash, &type);
|
const char* interned = symtab_add(lexer.lexing_start, len, hash, &type);
|
||||||
return make_string_token(type, interned);
|
return make_string_token(type, interned);
|
||||||
}
|
}
|
||||||
@@ -259,12 +265,6 @@ static inline void scan_skipped_ident()
|
|||||||
// we split identifiers into 2 types + find keywords.
|
// we split identifiers into 2 types + find keywords.
|
||||||
static inline Token scan_ident(void)
|
static inline Token scan_ident(void)
|
||||||
{
|
{
|
||||||
// If we're in ignore keywords state, simply skip stuff.
|
|
||||||
if (lexer.lexer_state == LEXER_STATE_DEFERED_PARSING)
|
|
||||||
{
|
|
||||||
scan_skipped_ident();
|
|
||||||
return make_token(TOKEN_IDENT);
|
|
||||||
}
|
|
||||||
|
|
||||||
TokenType type = 0;
|
TokenType type = 0;
|
||||||
uint32_t hash = FNV1_SEED;
|
uint32_t hash = FNV1_SEED;
|
||||||
@@ -323,7 +323,7 @@ static Token scan_oct(void)
|
|||||||
char o = next(); // Skip the o
|
char o = next(); // Skip the o
|
||||||
if (!is_oct(next())) return error_token("An expression starting with '0%c' would expect to be followed by octal numbers (0-7).", o);
|
if (!is_oct(next())) return error_token("An expression starting with '0%c' would expect to be followed by octal numbers (0-7).", o);
|
||||||
while (is_oct_or_(peek())) next();
|
while (is_oct_or_(peek())) next();
|
||||||
return make_token(TOKEN_INTEGER);
|
return make_token(TOKEN_INTEGER, lexer.lexing_start);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -336,7 +336,7 @@ Token scan_binary(void)
|
|||||||
"did you try to write a hex value but forgot the '0x'?", b);
|
"did you try to write a hex value but forgot the '0x'?", b);
|
||||||
}
|
}
|
||||||
while (is_binary_or_(peek())) next();
|
while (is_binary_or_(peek())) next();
|
||||||
return make_token(TOKEN_INTEGER);
|
return make_token(TOKEN_INTEGER, lexer.lexing_start);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define PARSE_SPECIAL_NUMBER(is_num, is_num_with_underscore, exp, EXP) \
|
#define PARSE_SPECIAL_NUMBER(is_num, is_num_with_underscore, exp, EXP) \
|
||||||
@@ -362,7 +362,7 @@ if (c == (exp) || c == (EXP)) \
|
|||||||
while (is_num(peek())) next(); \
|
while (is_num(peek())) next(); \
|
||||||
} \
|
} \
|
||||||
if (prev() == '_') return error_token("The number ended with '_', but that character needs to be between, not after, digits."); \
|
if (prev() == '_') return error_token("The number ended with '_', but that character needs to be between, not after, digits."); \
|
||||||
return make_token(is_float ? TOKEN_FLOAT : TOKEN_INTEGER)
|
return make_token(is_float ? TOKEN_FLOAT : TOKEN_INTEGER, lexer.lexing_start)
|
||||||
|
|
||||||
static inline Token scan_hex(void)
|
static inline Token scan_hex(void)
|
||||||
{
|
{
|
||||||
@@ -446,7 +446,7 @@ static inline Token scan_char()
|
|||||||
{
|
{
|
||||||
error_token("Character literals may only be 1, 2 or 8 characters wide.");
|
error_token("Character literals may only be 1, 2 or 8 characters wide.");
|
||||||
}
|
}
|
||||||
return make_token(TOKEN_INTEGER);
|
return make_token(TOKEN_INTEGER, lexer.lexing_start);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline Token scan_string()
|
static inline Token scan_string()
|
||||||
@@ -464,7 +464,7 @@ static inline Token scan_string()
|
|||||||
return error_token("Reached the end looking for '\"'. Did you forget it?");
|
return error_token("Reached the end looking for '\"'. Did you forget it?");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return make_token(TOKEN_STRING);
|
return make_token(TOKEN_STRING, lexer.lexing_start);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void skip_docs_whitespace()
|
static inline void skip_docs_whitespace()
|
||||||
@@ -489,7 +489,7 @@ static inline void skip_docs_whitespace()
|
|||||||
static inline Token scan_docs_directive(void)
|
static inline Token scan_docs_directive(void)
|
||||||
{
|
{
|
||||||
match_assert('@');
|
match_assert('@');
|
||||||
Token token = scan_prefixed_ident(TOKEN_AT_IDENT, TOKEN_AT, false);
|
Token token = scan_prefixed_ident(TOKEN_AT_IDENT, TOKEN_AT, false, "@");
|
||||||
assert(token.type != TOKEN_AT);
|
assert(token.type != TOKEN_AT);
|
||||||
lexer.lexer_state = LEXER_STATE_DOCS_PARSE_DIRECTIVE;
|
lexer.lexer_state = LEXER_STATE_DOCS_PARSE_DIRECTIVE;
|
||||||
return token;
|
return token;
|
||||||
@@ -516,7 +516,7 @@ static inline Token scan_docs(void)
|
|||||||
|
|
||||||
// Return end
|
// Return end
|
||||||
lexer.lexer_state = LEXER_STATE_NORMAL;
|
lexer.lexer_state = LEXER_STATE_NORMAL;
|
||||||
return make_token(TOKEN_DOCS_END);
|
return make_token(TOKEN_DOCS_END, "*/");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise continue consuming
|
// Otherwise continue consuming
|
||||||
@@ -548,14 +548,15 @@ static inline Token scan_docs(void)
|
|||||||
|
|
||||||
// We found the end, so just make a token out of the rest.
|
// We found the end, so just make a token out of the rest.
|
||||||
// Note that this line will not get a linebreak at the end.
|
// Note that this line will not get a linebreak at the end.
|
||||||
if (peek_next() == '/') return make_token(TOKEN_DOCS_LINE);
|
if (peek_next() == '/') return make_token(TOKEN_DOCS_LINE, "*");
|
||||||
// Otherwise it's just something in the text, so continue.
|
// Otherwise it's just something in the text, so continue.
|
||||||
next();
|
next();
|
||||||
break;
|
break;
|
||||||
case '\n':
|
case '\n':
|
||||||
// Normal line of text.
|
// Normal line of text.
|
||||||
|
lexer_store_line_end();
|
||||||
next();
|
next();
|
||||||
return make_token(TOKEN_DOCS_LINE);
|
return make_token(TOKEN_DOCS_LINE, "\n");
|
||||||
case '\0':
|
case '\0':
|
||||||
return error_token("The document ended without finding the end of the doc comment. "
|
return error_token("The document ended without finding the end of the doc comment. "
|
||||||
"Did you forget a '*/' somewhere?");
|
"Did you forget a '*/' somewhere?");
|
||||||
@@ -586,15 +587,15 @@ Token lexer_scan_token(void)
|
|||||||
// and switch state.
|
// and switch state.
|
||||||
skip(3);
|
skip(3);
|
||||||
lexer.lexer_state = LEXER_STATE_DOCS_PARSE;
|
lexer.lexer_state = LEXER_STATE_DOCS_PARSE;
|
||||||
return make_token(TOKEN_DOCS_START);
|
return make_token(TOKEN_DOCS_START, "/**");
|
||||||
case WHITESPACE_COMMENT_REACHED_EOF:
|
case WHITESPACE_COMMENT_REACHED_EOF:
|
||||||
return error_token("Reached the end looking for '*/'. Did you forget it somewhere?");
|
return error_token("Reached the end looking for '*/'. Did you forget it somewhere?");
|
||||||
case WHITESPACE_FOUND_EOF:
|
case WHITESPACE_FOUND_EOF:
|
||||||
return make_token(TOKEN_EOF);
|
return make_token(TOKEN_EOF, "\n");
|
||||||
case WHITESPACE_FOUND_DOCS_EOL:
|
case WHITESPACE_FOUND_DOCS_EOL:
|
||||||
skip(1);
|
skip(1);
|
||||||
lexer.lexer_state = LEXER_STATE_DOCS_PARSE;
|
lexer.lexer_state = LEXER_STATE_DOCS_PARSE;
|
||||||
return make_token(TOKEN_DOCS_EOL);
|
return make_token(TOKEN_DOCS_EOL, "\n");
|
||||||
case WHITESPACE_SKIPPED_OK:
|
case WHITESPACE_SKIPPED_OK:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -603,78 +604,78 @@ Token lexer_scan_token(void)
|
|||||||
switch (c)
|
switch (c)
|
||||||
{
|
{
|
||||||
case '@':
|
case '@':
|
||||||
return scan_prefixed_ident(TOKEN_AT_IDENT, TOKEN_AT, true);
|
return scan_prefixed_ident(TOKEN_AT_IDENT, TOKEN_AT, true, "@");
|
||||||
case '\'':
|
case '\'':
|
||||||
return scan_char();
|
return scan_char();
|
||||||
case '"':
|
case '"':
|
||||||
return scan_string();
|
return scan_string();
|
||||||
case '#':
|
case '#':
|
||||||
return scan_prefixed_ident(TOKEN_HASH_IDENT, TOKEN_HASH, false);
|
return scan_prefixed_ident(TOKEN_HASH_IDENT, TOKEN_HASH, false, "#");
|
||||||
case '$':
|
case '$':
|
||||||
return scan_prefixed_ident(TOKEN_CT_IDENT, TOKEN_DOLLAR, false);
|
return scan_prefixed_ident(TOKEN_CT_IDENT, TOKEN_DOLLAR, false, "$");
|
||||||
case ',':
|
case ',':
|
||||||
return make_token(TOKEN_COMMA);
|
return make_token(TOKEN_COMMA, ",");
|
||||||
case ';':
|
case ';':
|
||||||
return make_token(TOKEN_EOS);
|
return make_token(TOKEN_EOS, ";");
|
||||||
case '{':
|
case '{':
|
||||||
return make_token(TOKEN_LBRACE);
|
return make_token(TOKEN_LBRACE, "{");
|
||||||
case '}':
|
case '}':
|
||||||
return make_token(match(')') ? TOKEN_RPARBRA : TOKEN_RBRACE);
|
return match(')') ? make_token(TOKEN_RPARBRA, "})") : make_token(TOKEN_RBRACE, "})");
|
||||||
case '(':
|
case '(':
|
||||||
return make_token(match('{') ? TOKEN_LPARBRA : TOKEN_LPAREN);
|
return match('{') ? make_token(TOKEN_LPARBRA, "({") : make_token(TOKEN_LPAREN, ")");
|
||||||
case ')':
|
case ')':
|
||||||
return make_token(TOKEN_RPAREN);
|
return make_token(TOKEN_RPAREN, ")");
|
||||||
case '[':
|
case '[':
|
||||||
return make_token(TOKEN_LBRACKET);
|
return make_token(TOKEN_LBRACKET, "[");
|
||||||
case ']':
|
case ']':
|
||||||
return make_token(TOKEN_RBRACKET);
|
return make_token(TOKEN_RBRACKET, "]");
|
||||||
case '.':
|
case '.':
|
||||||
if (match('.')) return make_token(match('.') ? TOKEN_ELIPSIS : TOKEN_DOTDOT);
|
if (match('.')) return match('.') ? make_token(TOKEN_ELIPSIS, "...") : make_token(TOKEN_DOTDOT, "..");
|
||||||
return make_token(TOKEN_DOT);
|
return make_token(TOKEN_DOT, ".");
|
||||||
case '~':
|
case '~':
|
||||||
return make_token(TOKEN_BIT_NOT);
|
return make_token(TOKEN_BIT_NOT, "~");
|
||||||
case ':':
|
case ':':
|
||||||
return make_token(match(':') ? TOKEN_SCOPE : TOKEN_COLON);
|
return match(':') ? make_token(TOKEN_SCOPE, "::") : make_token(TOKEN_COLON, ":");
|
||||||
case '!':
|
case '!':
|
||||||
return make_token(match('=') ? TOKEN_NOT_EQUAL : TOKEN_NOT);
|
return match('=') ? make_token(TOKEN_NOT_EQUAL, "!=") : make_token(TOKEN_NOT, "!");
|
||||||
case '/':
|
case '/':
|
||||||
return make_token(match('=') ? TOKEN_DIV_ASSIGN : TOKEN_DIV);
|
return match('=') ? make_token(TOKEN_DIV_ASSIGN, "/=") : make_token(TOKEN_DIV, "/");
|
||||||
case '*':
|
case '*':
|
||||||
if (lexer.lexer_state == LEXER_STATE_DOCS_PARSE_DIRECTIVE && match('/'))
|
if (lexer.lexer_state == LEXER_STATE_DOCS_PARSE_DIRECTIVE && match('/'))
|
||||||
{
|
{
|
||||||
lexer.lexer_state = LEXER_STATE_NORMAL;
|
lexer.lexer_state = LEXER_STATE_NORMAL;
|
||||||
return make_token(TOKEN_DOCS_END);
|
return make_token(TOKEN_DOCS_END, "*/");
|
||||||
}
|
}
|
||||||
return make_token(match('=') ? TOKEN_MULT_ASSIGN : TOKEN_STAR);
|
return match('=') ? make_token(TOKEN_MULT_ASSIGN, "*=") : make_token(TOKEN_STAR, "*");
|
||||||
case '=':
|
case '=':
|
||||||
return make_token(match('=') ? TOKEN_EQEQ : TOKEN_EQ);
|
return match('=') ? make_token(TOKEN_EQEQ, "==") : make_token(TOKEN_EQ, "=");
|
||||||
case '^':
|
case '^':
|
||||||
return make_token(match('=') ? TOKEN_BIT_XOR_ASSIGN : TOKEN_BIT_XOR);
|
return match('=') ? make_token(TOKEN_BIT_XOR_ASSIGN, "^=") : make_token(TOKEN_BIT_XOR, "^");
|
||||||
case '?':
|
case '?':
|
||||||
return make_token(match(':') ? TOKEN_ELVIS : TOKEN_QUESTION);
|
return match(':') ? make_token(TOKEN_EQEQ, "?:") : make_token(TOKEN_EQ, "?");
|
||||||
case '<':
|
case '<':
|
||||||
if (match('<')) return make_token(match('=') ? TOKEN_SHL_ASSIGN : TOKEN_SHL);
|
if (match('<')) return match('=') ? make_token(TOKEN_SHL_ASSIGN, "<<=") : make_token(TOKEN_SHL, "<<");
|
||||||
return make_token(match('=') ? TOKEN_LESS_EQ : TOKEN_LESS);
|
return match('=') ? make_token(TOKEN_LESS_EQ, "<=") : make_token(TOKEN_LESS, "<");
|
||||||
case '>':
|
case '>':
|
||||||
if (match('>')) return make_token(match('=') ? TOKEN_SHR_ASSIGN : TOKEN_SHR);
|
if (match('>')) return match('=') ? make_token(TOKEN_SHR_ASSIGN, ">>=") : make_token(TOKEN_SHR, ">>");
|
||||||
return make_token(match('=') ? TOKEN_GREATER_EQ : TOKEN_GREATER);
|
return match('=') ? make_token(TOKEN_GREATER_EQ, ">=") : make_token(TOKEN_GREATER, ">");
|
||||||
case '%':
|
case '%':
|
||||||
return make_token(match('=') ? TOKEN_MOD_ASSIGN : TOKEN_MOD);
|
return match('=') ? make_token(TOKEN_MOD_ASSIGN, "%=") : make_token(TOKEN_MOD, "%");
|
||||||
case '&':
|
case '&':
|
||||||
if (match('&')) return make_token(match('=') ? TOKEN_AND_ASSIGN : TOKEN_AND);
|
if (match('&')) return match('=') ? make_token(TOKEN_AND_ASSIGN, "&&=") : make_token(TOKEN_AND, "&&");
|
||||||
return make_token(match('=') ? TOKEN_BIT_AND_ASSIGN : TOKEN_AMP);
|
return match('=') ? make_token(TOKEN_BIT_AND_ASSIGN, "&=") : make_token(TOKEN_AMP, "&");
|
||||||
case '|':
|
case '|':
|
||||||
if (match('|')) return make_token(match('=') ? TOKEN_OR_ASSIGN : TOKEN_OR);
|
if (match('|')) return match('=') ? make_token(TOKEN_OR_ASSIGN, "||=") : make_token(TOKEN_OR, "||");
|
||||||
return make_token(match('=') ? TOKEN_BIT_OR_ASSIGN : TOKEN_BIT_OR);
|
return match('=') ? make_token(TOKEN_BIT_OR_ASSIGN, "|=") : make_token(TOKEN_BIT_OR, "|");
|
||||||
case '+':
|
case '+':
|
||||||
if (match('+')) return make_token(TOKEN_PLUSPLUS);
|
if (match('+')) return make_token(TOKEN_PLUSPLUS, "++");
|
||||||
if (match('=')) return make_token(TOKEN_PLUS_ASSIGN);
|
if (match('=')) return make_token(TOKEN_PLUS_ASSIGN, "+=");
|
||||||
return make_token(TOKEN_PLUS);
|
return make_token(TOKEN_PLUS, "+");
|
||||||
case '-':
|
case '-':
|
||||||
if (match('>')) return make_token(TOKEN_ARROW);
|
if (match('>')) return make_token(TOKEN_ARROW, "->");
|
||||||
if (match('-')) return make_token(TOKEN_MINUSMINUS);
|
if (match('-')) return make_token(TOKEN_MINUSMINUS, "--");
|
||||||
if (match('=')) return make_token(TOKEN_MINUS_ASSIGN);
|
if (match('=')) return make_token(TOKEN_MINUS_ASSIGN, "-=");
|
||||||
return make_token(TOKEN_MINUS);
|
return make_token(TOKEN_MINUS, "-");
|
||||||
default:
|
default:
|
||||||
if (is_alphanum_(c))
|
if (is_alphanum_(c))
|
||||||
{
|
{
|
||||||
|
|||||||
136
src/compiler/llvm_codegen.c
Normal file
136
src/compiler/llvm_codegen.c
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
#include "llvm_codegen_internal.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static void gencontext_init(GenContext *context, Context *ast_context)
|
||||||
|
{
|
||||||
|
memset(context, 0, sizeof(GenContext));
|
||||||
|
context->context = LLVMContextCreate();
|
||||||
|
context->ast_context = ast_context;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gencontext_destroy(GenContext *context)
|
||||||
|
{
|
||||||
|
LLVMContextDispose(context->context);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static LLVMValueRef gencontext_emit_null_constant(GenContext *context, Type *type)
|
||||||
|
{
|
||||||
|
TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
static LLVMValueRef gencontext_emit_initializer(GenContext *context, Expr *expr)
|
||||||
|
{
|
||||||
|
TODO
|
||||||
|
}
|
||||||
|
static void gencontext_emit_global_variable_definition(GenContext *context, Decl *decl, bool is_tentative)
|
||||||
|
{
|
||||||
|
assert(decl->var.kind == VARDECL_GLOBAL);
|
||||||
|
|
||||||
|
LLVMValueRef init = NULL;
|
||||||
|
|
||||||
|
if (!decl->var.init_expr)
|
||||||
|
{
|
||||||
|
// Tentative definition, initialized to zero, but only
|
||||||
|
// emitted at the end of the translation unit.
|
||||||
|
init = gencontext_emit_null_constant(context, decl->var.type);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
init = gencontext_emit_initializer(context, decl->var.init_expr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO fix name
|
||||||
|
decl->var.backend_ref = LLVMAddGlobal(context->module, gencontext_get_llvm_type(context, decl->var.type), decl->name.string);
|
||||||
|
|
||||||
|
// If read only: LLVMSetGlobalConstant(decl->var.backend_ref, 1);
|
||||||
|
|
||||||
|
switch (decl->visibility)
|
||||||
|
{
|
||||||
|
case VISIBLE_MODULE:
|
||||||
|
LLVMSetVisibility(decl->var.backend_ref, LLVMProtectedVisibility);
|
||||||
|
break;
|
||||||
|
case VISIBLE_PUBLIC:
|
||||||
|
LLVMSetVisibility(decl->var.backend_ref, LLVMDefaultVisibility);
|
||||||
|
break;
|
||||||
|
case VISIBLE_LOCAL:
|
||||||
|
LLVMSetVisibility(decl->var.backend_ref, LLVMHiddenVisibility);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int alignment = 64; // TODO
|
||||||
|
// Should we set linkage here?
|
||||||
|
if (context->debug.builder)
|
||||||
|
{
|
||||||
|
decl->var.backend_debug_ref = LLVMDIBuilderCreateGlobalVariableExpression(context->debug.builder,
|
||||||
|
NULL /*scope*/,
|
||||||
|
decl->name.string,
|
||||||
|
decl->name.span.length,
|
||||||
|
"linkagename",
|
||||||
|
2,
|
||||||
|
context->debug.file,
|
||||||
|
12 /* lineno */,
|
||||||
|
decl->var.type->backend_debug_type,
|
||||||
|
decl->visibility ==
|
||||||
|
VISIBLE_LOCAL, /* expr */
|
||||||
|
NULL, /** declaration **/
|
||||||
|
NULL,
|
||||||
|
alignment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static void gencontext_verify_ir(GenContext *context)
|
||||||
|
{
|
||||||
|
char *error = NULL;
|
||||||
|
assert(context->module);
|
||||||
|
LLVMVerifyModule(context->module, LLVMPrintMessageAction, &error);
|
||||||
|
if (error)
|
||||||
|
{
|
||||||
|
if (*error)
|
||||||
|
{
|
||||||
|
error_exit("Could not verify IR: %s", error);
|
||||||
|
}
|
||||||
|
error_exit("Could not verify module IR.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void gencontext_print_llvm_ir(GenContext *context)
|
||||||
|
{
|
||||||
|
char *err = NULL;
|
||||||
|
char *filename = strformat("xx.llvmir" /*, context->module_name*/);
|
||||||
|
if (LLVMPrintModuleToFile(context->module, filename, &err) != 0)
|
||||||
|
{
|
||||||
|
error_exit("Could not emit ir to file: %s", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
LLVMValueRef gencontext_emit_alloca(GenContext *context, Decl *decl)
|
||||||
|
{
|
||||||
|
LLVMBasicBlockRef current_block = LLVMGetInsertBlock(context->builder);
|
||||||
|
LLVMPositionBuilderBefore(context->builder, context->alloca_point);
|
||||||
|
LLVMValueRef alloca = LLVMBuildAlloca(context->builder, gencontext_get_llvm_type(context, decl->var.type->canonical), decl->name.string);
|
||||||
|
LLVMPositionBuilderAtEnd(context->builder, current_block);
|
||||||
|
return alloca;
|
||||||
|
}
|
||||||
|
|
||||||
|
void llvm_codegen(Context *context)
|
||||||
|
{
|
||||||
|
GenContext gen_context;
|
||||||
|
gencontext_init(&gen_context, context);
|
||||||
|
gencontext_begin_module(&gen_context);
|
||||||
|
// EmitDeferred()
|
||||||
|
VECEACH(context->functions, i)
|
||||||
|
{
|
||||||
|
gencontext_emit_function_decl(&gen_context, context->functions[i]);
|
||||||
|
}
|
||||||
|
LLVMDumpModule(gen_context.module);
|
||||||
|
gencontext_print_llvm_ir(&gen_context);
|
||||||
|
LLVMDumpModule(gen_context.module);
|
||||||
|
gencontext_end_module(&gen_context);
|
||||||
|
gencontext_destroy(&gen_context);
|
||||||
|
}
|
||||||
83
src/compiler/llvm_codegen_debug_info.c
Normal file
83
src/compiler/llvm_codegen_debug_info.c
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
#include "llvm_codegen_internal.h"
|
||||||
|
|
||||||
|
void gencontext_set_debug_location(GenContext *context, SourceRange source_range)
|
||||||
|
{
|
||||||
|
if (source_range.loc == INVALID_LOC) return;
|
||||||
|
|
||||||
|
context->debug.current_range = source_range;
|
||||||
|
#ifdef TODOLATER
|
||||||
|
CurLoc = CGM.getContext().getSourceManager().getExpansionLoc(Loc);
|
||||||
|
#endif
|
||||||
|
// If we've changed files in the middle of a lexical scope go ahead
|
||||||
|
// and create a new lexical scope with file node if it's different
|
||||||
|
// from the one in the scope.
|
||||||
|
if (!vec_size(context->debug.lexical_block_stack)) return;
|
||||||
|
|
||||||
|
#ifdef TODOLATE
|
||||||
|
if (auto *LBF = dyn_cast<llvm::DILexicalBlockFile>(Scope)) {
|
||||||
|
LexicalBlockStack.pop_back();
|
||||||
|
LexicalBlockStack.emplace_back(DBuilder.createLexicalBlockFile(
|
||||||
|
LBF->getScope(), getOrCreateFile(CurLoc)));
|
||||||
|
} else if (isa<llvm::DILexicalBlock>(Scope) ||
|
||||||
|
isa<llvm::DISubprogram>(Scope)) {
|
||||||
|
LexicalBlockStack.pop_back();
|
||||||
|
LexicalBlockStack.emplace_back(
|
||||||
|
DBuilder.createLexicalBlockFile(Scope, getOrCreateFile(CurLoc)));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void gencontext_emit_debug_location(GenContext *context, SourceRange location)
|
||||||
|
{
|
||||||
|
gencontext_set_debug_location(context, location);
|
||||||
|
|
||||||
|
if (context->debug.current_range.loc == INVALID_LOC || vec_size(context->debug.lexical_block_stack) == 0) return;
|
||||||
|
|
||||||
|
LLVMMetadataRef scope = VECLAST(context->debug.lexical_block_stack);
|
||||||
|
LLVMMetadataRef debug_location = LLVMDIBuilderCreateDebugLocation(context->context, 320, 12, scope, context->debug.inlined_at);
|
||||||
|
LLVMSetCurrentDebugLocation2(context->builder, debug_location);
|
||||||
|
}
|
||||||
|
|
||||||
|
static LLVMMetadataRef gencontext_simple_debug_type(GenContext *context, Type *type, int dwarf_code)
|
||||||
|
{
|
||||||
|
return LLVMDIBuilderCreateBasicType(context->debug.builder,
|
||||||
|
type->name_loc.string,
|
||||||
|
type->name_loc.span.length,
|
||||||
|
type->builtin.bitsize,
|
||||||
|
dwarf_code, 0);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
LLVMMetadataRef gencontext_create_builtin_debug_type(GenContext *context, Type *builtin_type)
|
||||||
|
{
|
||||||
|
assert(builtin_type->canonical == builtin_type);
|
||||||
|
// Consider special handling of UTF8 arrays.
|
||||||
|
switch (builtin_type->type_kind)
|
||||||
|
{
|
||||||
|
case TYPE_BOOL:
|
||||||
|
return gencontext_simple_debug_type(context, builtin_type, DW_ATE_boolean);
|
||||||
|
case TYPE_I8:
|
||||||
|
return gencontext_simple_debug_type(context, builtin_type, DW_ATE_signed_char); // DW_ATE_UTF?
|
||||||
|
case TYPE_U8:
|
||||||
|
return gencontext_simple_debug_type(context, builtin_type, DW_ATE_unsigned_char);
|
||||||
|
case TYPE_I16:
|
||||||
|
case TYPE_I32:
|
||||||
|
case TYPE_I64:
|
||||||
|
return gencontext_simple_debug_type(context, builtin_type, DW_ATE_signed);
|
||||||
|
case TYPE_U16:
|
||||||
|
case TYPE_U32:
|
||||||
|
case TYPE_U64:
|
||||||
|
return gencontext_simple_debug_type(context, builtin_type, DW_ATE_unsigned);
|
||||||
|
case TYPE_F32:
|
||||||
|
case TYPE_F64:
|
||||||
|
return gencontext_simple_debug_type(context, builtin_type, DW_ATE_float);
|
||||||
|
case TYPE_VOID:
|
||||||
|
return NULL;
|
||||||
|
default:
|
||||||
|
UNREACHABLE
|
||||||
|
}
|
||||||
|
}
|
||||||
379
src/compiler/llvm_codegen_expr.c
Normal file
379
src/compiler/llvm_codegen_expr.c
Normal file
@@ -0,0 +1,379 @@
|
|||||||
|
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
#include "llvm_codegen_internal.h"
|
||||||
|
#include "compiler_internal.h"
|
||||||
|
|
||||||
|
LLVMValueRef gencontext_emit_address(GenContext *context, Expr *expr)
|
||||||
|
{
|
||||||
|
switch (expr->expr_kind)
|
||||||
|
{
|
||||||
|
case EXPR_IDENTIFIER:
|
||||||
|
return expr->identifier_expr.decl->var.backend_ref;
|
||||||
|
case EXPR_CONST:
|
||||||
|
case EXPR_TYPE:
|
||||||
|
UNREACHABLE
|
||||||
|
case EXPR_UNARY:
|
||||||
|
{
|
||||||
|
UnaryOp op = unaryop_from_token(expr->unary_expr.operator);
|
||||||
|
assert(op == UNARYOP_DEREF);
|
||||||
|
return gencontext_emit_expr(context, expr->unary_expr.expr);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
TODO;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline LLVMValueRef gencontext_emit_cast_expr(GenContext *context, Expr *expr)
|
||||||
|
{
|
||||||
|
LLVMValueRef rhs = gencontext_emit_expr(context, expr->expr_cast.expr);
|
||||||
|
switch (expr->expr_cast.kind)
|
||||||
|
{
|
||||||
|
case CAST_ERROR:
|
||||||
|
UNREACHABLE
|
||||||
|
case CAST_PTRPTR:
|
||||||
|
return LLVMBuildPointerCast(context->builder, rhs, LLVMTYPE(expr->type), "ptrptr");
|
||||||
|
case CAST_PTRXI:
|
||||||
|
return LLVMBuildPtrToInt(context->builder, rhs, LLVMTYPE(expr->type), "ptrxi");
|
||||||
|
case CAST_VARRPTR:
|
||||||
|
TODO
|
||||||
|
case CAST_ARRPTR:
|
||||||
|
TODO
|
||||||
|
case CAST_STRPTR:
|
||||||
|
TODO
|
||||||
|
case CAST_PTRBOOL:
|
||||||
|
return LLVMBuildICmp(context->builder, LLVMIntNE, rhs, LLVMConstPointerNull(LLVMTYPE(expr->type->canonical->base)), "ptrbool");
|
||||||
|
case CAST_BOOLINT:
|
||||||
|
return LLVMBuildTrunc(context->builder, rhs, LLVMTYPE(expr->type), "boolsi");
|
||||||
|
case CAST_FPBOOL:
|
||||||
|
return LLVMBuildFCmp(context->builder, LLVMRealUNE, rhs, LLVMConstNull(LLVMTypeOf(rhs)), "fpbool");
|
||||||
|
case CAST_BOOLFP:
|
||||||
|
return LLVMBuildSIToFP(context->builder, rhs, LLVMTYPE(expr->type), "boolfp");
|
||||||
|
case CAST_INTBOOL:
|
||||||
|
return LLVMBuildICmp(context->builder, LLVMIntNE, rhs, LLVMConstNull(LLVMTypeOf(rhs)), "intbool");
|
||||||
|
case CAST_FPFP:
|
||||||
|
return type_convert_will_trunc(expr->type, expr->expr_cast.expr->type)
|
||||||
|
? LLVMBuildFPTrunc(context->builder, rhs, LLVMTYPE(expr->type), "fpfptrunc")
|
||||||
|
: LLVMBuildFPExt(context->builder, rhs, LLVMTYPE(expr->type), "fpfpext");
|
||||||
|
case CAST_FPSI:
|
||||||
|
return LLVMBuildFPToSI(context->builder, rhs, LLVMTYPE(expr->type), "fpsi");
|
||||||
|
case CAST_FPUI:
|
||||||
|
return LLVMBuildFPToUI(context->builder, rhs, LLVMTYPE(expr->type), "fpui");
|
||||||
|
case CAST_SISI:
|
||||||
|
return type_convert_will_trunc(expr->type, expr->expr_cast.expr->type)
|
||||||
|
? LLVMBuildTrunc(context->builder, rhs, LLVMTYPE(expr->type), "sisitrunc")
|
||||||
|
: LLVMBuildSExt(context->builder, rhs, LLVMTYPE(expr->type), "sisiext");
|
||||||
|
case CAST_SIUI:
|
||||||
|
return type_convert_will_trunc(expr->type, expr->expr_cast.expr->type)
|
||||||
|
? LLVMBuildTrunc(context->builder, rhs, LLVMTYPE(expr->type), "siuitrunc")
|
||||||
|
: LLVMBuildZExt(context->builder, rhs, LLVMTYPE(expr->type), "siuiext");
|
||||||
|
break;
|
||||||
|
case CAST_SIFP:
|
||||||
|
return LLVMBuildSIToFP(context->builder, rhs, LLVMTYPE(expr->type), "sifp");
|
||||||
|
case CAST_XIPTR:
|
||||||
|
return LLVMBuildIntToPtr(context->builder, rhs, LLVMTYPE(expr->type), "xiptr");
|
||||||
|
case CAST_UISI:
|
||||||
|
return type_convert_will_trunc(expr->type, expr->expr_cast.expr->type)
|
||||||
|
? LLVMBuildTrunc(context->builder, rhs, LLVMTYPE(expr->type), "uisitrunc")
|
||||||
|
: LLVMBuildZExt(context->builder, rhs, LLVMTYPE(expr->type), "uisiext");
|
||||||
|
case CAST_UIUI:
|
||||||
|
return type_convert_will_trunc(expr->type, expr->expr_cast.expr->type)
|
||||||
|
? LLVMBuildTrunc(context->builder, rhs, LLVMTYPE(expr->type), "uiuitrunc")
|
||||||
|
: LLVMBuildZExt(context->builder, rhs, LLVMTYPE(expr->type), "uiuiext");
|
||||||
|
case CAST_UIFP:
|
||||||
|
return LLVMBuildUIToFP(context->builder, rhs, LLVMTYPE(expr->type), "uifp");
|
||||||
|
case CAST_ENUMSI:
|
||||||
|
TODO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LLVMValueRef gencontext_emit_unary_expr(GenContext *context, Expr *expr)
|
||||||
|
{
|
||||||
|
UnaryOp unary_op = unaryop_from_token(expr->unary_expr.operator);
|
||||||
|
switch (unary_op)
|
||||||
|
{
|
||||||
|
case UNARYOP_ERROR:
|
||||||
|
FATAL_ERROR("Illegal unary op %s", expr->unary_expr.operator);
|
||||||
|
case UNARYOP_NOT:
|
||||||
|
return LLVMBuildXor(context->builder, gencontext_emit_expr(context, expr->unary_expr.expr), LLVMConstInt(type_bool->backend_type, 1, 0), "not");
|
||||||
|
case UNARYOP_BITNEG:
|
||||||
|
return LLVMBuildNot(context->builder, gencontext_emit_expr(context, expr->unary_expr.expr), "bnot");
|
||||||
|
case UNARYOP_NEG:
|
||||||
|
if (type_is_float(expr->unary_expr.expr->type->canonical))
|
||||||
|
{
|
||||||
|
return LLVMBuildFNeg(context->builder, gencontext_emit_expr(context, expr->unary_expr.expr), "fneg");
|
||||||
|
}
|
||||||
|
return LLVMBuildNeg(context->builder, gencontext_emit_expr(context, expr->unary_expr.expr), "neg");
|
||||||
|
case UNARYOP_ADDR:
|
||||||
|
return gencontext_emit_address(context, expr->unary_expr.expr);
|
||||||
|
case UNARYOP_DEREF:
|
||||||
|
return LLVMBuildLoad(context->builder, gencontext_emit_expr(context, expr->unary_expr.expr), "deref");
|
||||||
|
case UNARYOP_INC:
|
||||||
|
TODO
|
||||||
|
case UNARYOP_DEC:
|
||||||
|
TODO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static LLVMValueRef gencontext_emit_assign(GenContext *context, Expr *left, LLVMValueRef right)
|
||||||
|
{
|
||||||
|
LLVMValueRef addr = gencontext_emit_address(context, left);
|
||||||
|
return LLVMBuildStore(context->builder, right, addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static LLVMValueRef gencontext_emit_logical_and_or(GenContext *context, Expr *expr, BinaryOp op)
|
||||||
|
{
|
||||||
|
// Value *ScalarExprEmitter::VisitBinLAnd(const BinaryOperator *E)
|
||||||
|
// For vector implementation.
|
||||||
|
|
||||||
|
// Set up basic blocks, following Cone
|
||||||
|
LLVMBasicBlockRef start_block = LLVMGetInsertBlock(context->builder);
|
||||||
|
LLVMBasicBlockRef phi_block = LLVMCreateBasicBlockInContext(context->context, op == BINARYOP_AND ? "and.phi" : "or.phi");
|
||||||
|
LLVMBasicBlockRef rhs_block = LLVMCreateBasicBlockInContext(context->context, op == BINARYOP_AND ? "and.rhs" : "or.rhs");
|
||||||
|
|
||||||
|
// Generate left-hand condition and conditional branch
|
||||||
|
LLVMValueRef lhs = gencontext_emit_expr(context, expr->binary_expr.left);
|
||||||
|
|
||||||
|
if (op == BINARYOP_AND)
|
||||||
|
{
|
||||||
|
gencontext_emit_cond_br(context, lhs, rhs_block, phi_block);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gencontext_emit_cond_br(context, lhs, phi_block, rhs_block);
|
||||||
|
}
|
||||||
|
|
||||||
|
gencontext_emit_block(context, rhs_block);
|
||||||
|
LLVMValueRef rhs = gencontext_emit_expr(context, expr->binary_expr.right);
|
||||||
|
gencontext_emit_br(context, phi_block);
|
||||||
|
|
||||||
|
// Generate phi
|
||||||
|
gencontext_emit_block(context, phi_block);
|
||||||
|
LLVMValueRef phi = LLVMBuildPhi(context->builder, type_bool->backend_type, "val");
|
||||||
|
|
||||||
|
// Simplify for LLVM by entering the constants we already know of.
|
||||||
|
LLVMValueRef result_on_skip = LLVMConstInt(LLVMInt1TypeInContext(context->context), op == BINARYOP_AND ? 0 : 1, false);
|
||||||
|
LLVMValueRef logicValues[2] = { result_on_skip, rhs };
|
||||||
|
LLVMBasicBlockRef blocks[2] = { start_block, rhs_block };
|
||||||
|
LLVMAddIncoming(phi, logicValues, blocks, 2);
|
||||||
|
|
||||||
|
return phi;
|
||||||
|
}
|
||||||
|
|
||||||
|
static LLVMValueRef gencontext_emit_binary(GenContext *context, Expr *expr, bool load_lhs_after_rhs, BinaryOp binary_op)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (binary_op == BINARYOP_AND || binary_op == BINARYOP_OR)
|
||||||
|
{
|
||||||
|
return gencontext_emit_logical_and_or(context, expr, binary_op);
|
||||||
|
}
|
||||||
|
Type *type = expr->type->canonical;
|
||||||
|
Expr *lhs = expr->binary_expr.left;
|
||||||
|
Expr *rhs = expr->binary_expr.right;
|
||||||
|
|
||||||
|
LLVMValueRef lhs_value;
|
||||||
|
LLVMValueRef rhs_value;
|
||||||
|
if (load_lhs_after_rhs)
|
||||||
|
{
|
||||||
|
rhs_value = gencontext_emit_expr(context, rhs);
|
||||||
|
lhs_value = gencontext_emit_expr(context, lhs);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lhs_value = gencontext_emit_expr(context, lhs);
|
||||||
|
rhs_value = gencontext_emit_expr(context, rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_float = type_is_float(type);
|
||||||
|
|
||||||
|
switch (binary_op)
|
||||||
|
{
|
||||||
|
case BINARYOP_ERROR:
|
||||||
|
UNREACHABLE
|
||||||
|
case BINARYOP_MULT:
|
||||||
|
return is_float ? LLVMBuildFMul(context->builder, lhs_value, rhs_value, "fmul") : LLVMBuildMul(context->builder, lhs_value, rhs_value, "mul");
|
||||||
|
case BINARYOP_SUB:
|
||||||
|
if (lhs->type->canonical->type_kind == TYPE_POINTER)
|
||||||
|
{
|
||||||
|
if (lhs->type->canonical == rhs->type->canonical) return LLVMBuildPtrDiff(context->builder, lhs_value, rhs_value, "ptrdiff");
|
||||||
|
rhs_value = LLVMBuildNeg(context->builder, rhs_value, "");
|
||||||
|
return LLVMBuildGEP2(context->builder, gencontext_get_llvm_type(context, lhs->type->canonical), lhs_value, &rhs_value, 1, "ptrsub");
|
||||||
|
}
|
||||||
|
if (is_float) return LLVMBuildFSub(context->builder, lhs_value, rhs_value, "fsub");
|
||||||
|
// Consider UB version instead.
|
||||||
|
return LLVMBuildSub(context->builder, lhs_value, rhs_value, "sub");
|
||||||
|
case BINARYOP_ADD:
|
||||||
|
if (lhs->type->canonical->type_kind == TYPE_POINTER)
|
||||||
|
{
|
||||||
|
assert(type_is_integer(lhs->type->canonical));
|
||||||
|
return LLVMBuildGEP2(context->builder, gencontext_get_llvm_type(context, lhs->type->canonical), lhs_value, &rhs_value, 1, "ptradd");
|
||||||
|
}
|
||||||
|
if (is_float) return LLVMBuildFAdd(context->builder, lhs_value, rhs_value, "fadd");
|
||||||
|
// Consider UB version instead.
|
||||||
|
return LLVMBuildAdd(context->builder, lhs_value, rhs_value, "add");
|
||||||
|
case BINARYOP_DIV:
|
||||||
|
if (is_float) return LLVMBuildFDiv(context->builder, lhs_value, rhs_value, "fdiv");
|
||||||
|
return type_is_unsigned(type)
|
||||||
|
? LLVMBuildUDiv(context->builder, lhs_value, rhs_value, "udiv")
|
||||||
|
: LLVMBuildSDiv(context->builder, lhs_value, rhs_value, "sdiv");
|
||||||
|
case BINARYOP_MOD:
|
||||||
|
return type_is_unsigned(type)
|
||||||
|
? LLVMBuildURem(context->builder, lhs_value, rhs_value, "umod")
|
||||||
|
: LLVMBuildSRem(context->builder, lhs_value, rhs_value, "smod");
|
||||||
|
case BINARYOP_SHR:
|
||||||
|
return type_is_unsigned(type)
|
||||||
|
? LLVMBuildLShr(context->builder, lhs_value, rhs_value, "lshr")
|
||||||
|
: LLVMBuildAShr(context->builder, lhs_value, rhs_value, "ashr");
|
||||||
|
case BINARYOP_SHL:
|
||||||
|
return LLVMBuildShl(context->builder, lhs_value, rhs_value, "shr");
|
||||||
|
case BINARYOP_BIT_AND:
|
||||||
|
return LLVMBuildAnd(context->builder, lhs_value, rhs_value, "and");
|
||||||
|
case BINARYOP_BIT_OR:
|
||||||
|
return LLVMBuildOr(context->builder, lhs_value, rhs_value, "or");
|
||||||
|
case BINARYOP_BIT_XOR:
|
||||||
|
return LLVMBuildXor(context->builder, lhs_value, rhs_value, "xor");
|
||||||
|
case BINARYOP_EQ:
|
||||||
|
// Unordered?
|
||||||
|
if (type_is_float(type)) LLVMBuildFCmp(context->builder, LLVMRealUEQ, lhs_value, rhs_value, "eq");
|
||||||
|
return LLVMBuildICmp(context->builder, LLVMIntEQ, lhs_value, rhs_value, "eq");
|
||||||
|
case BINARYOP_NE:
|
||||||
|
// Unordered?
|
||||||
|
if (type_is_float(type)) LLVMBuildFCmp(context->builder, LLVMRealUNE, lhs_value, rhs_value, "neq");
|
||||||
|
return LLVMBuildICmp(context->builder, LLVMIntNE, lhs_value, rhs_value, "neq");
|
||||||
|
case BINARYOP_GE:
|
||||||
|
if (type_is_float(type)) LLVMBuildFCmp(context->builder, LLVMRealUGE, lhs_value, rhs_value, "ge");
|
||||||
|
return type_is_unsigned(type)
|
||||||
|
? LLVMBuildICmp(context->builder, LLVMIntUGE, lhs_value, rhs_value, "ge")
|
||||||
|
: LLVMBuildICmp(context->builder, LLVMIntSGE, lhs_value, rhs_value, "ge");
|
||||||
|
case BINARYOP_GT:
|
||||||
|
if (type_is_float(type)) LLVMBuildFCmp(context->builder, LLVMRealUGT, lhs_value, rhs_value, "gt");
|
||||||
|
return type_is_unsigned(type)
|
||||||
|
? LLVMBuildICmp(context->builder, LLVMIntUGT, lhs_value, rhs_value, "gt")
|
||||||
|
: LLVMBuildICmp(context->builder, LLVMIntSGT, lhs_value, rhs_value, "gt");
|
||||||
|
case BINARYOP_LE:
|
||||||
|
if (type_is_float(type)) LLVMBuildFCmp(context->builder, LLVMRealULE, lhs_value, rhs_value, "le");
|
||||||
|
return type_is_unsigned(type)
|
||||||
|
? LLVMBuildICmp(context->builder, LLVMIntULE, lhs_value, rhs_value, "le")
|
||||||
|
: LLVMBuildICmp(context->builder, LLVMIntSLE, lhs_value, rhs_value, "le");
|
||||||
|
case BINARYOP_LT:
|
||||||
|
if (type_is_float(type)) LLVMBuildFCmp(context->builder, LLVMRealULE, lhs_value, rhs_value, "lt");
|
||||||
|
return type_is_unsigned(type)
|
||||||
|
? LLVMBuildICmp(context->builder, LLVMIntULT, lhs_value, rhs_value, "lt")
|
||||||
|
: LLVMBuildICmp(context->builder, LLVMIntSLT, lhs_value, rhs_value, "lt");
|
||||||
|
case BINARYOP_AND:
|
||||||
|
case BINARYOP_OR:
|
||||||
|
UNREACHABLE
|
||||||
|
case BINARYOP_ASSIGN:
|
||||||
|
case BINARYOP_MULT_ASSIGN:
|
||||||
|
case BINARYOP_ADD_ASSIGN:
|
||||||
|
case BINARYOP_SUB_ASSIGN:
|
||||||
|
case BINARYOP_DIV_ASSIGN:
|
||||||
|
case BINARYOP_MOD_ASSIGN:
|
||||||
|
case BINARYOP_AND_ASSIGN:
|
||||||
|
case BINARYOP_OR_ASSIGN:
|
||||||
|
case BINARYOP_BIT_AND_ASSIGN:
|
||||||
|
case BINARYOP_BIT_OR_ASSIGN:
|
||||||
|
case BINARYOP_BIT_XOR_ASSIGN:
|
||||||
|
case BINARYOP_SHR_ASSIGN:
|
||||||
|
case BINARYOP_SHL_ASSIGN:
|
||||||
|
UNREACHABLE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static LLVMValueRef gencontext_emit_binary_expr(GenContext *context, Expr *expr)
|
||||||
|
{
|
||||||
|
BinaryOp binary_op = binaryop_from_token(expr->binary_expr.operator);
|
||||||
|
if (binary_op > BINARYOP_ASSIGN)
|
||||||
|
{
|
||||||
|
BinaryOp base_op = binaryop_assign_base_op(binary_op);
|
||||||
|
assert(base_op != BINARYOP_ERROR);
|
||||||
|
LLVMValueRef value = gencontext_emit_binary(context, expr, true, base_op);
|
||||||
|
gencontext_emit_assign(context, expr->binary_expr.left, value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
if (binary_op == BINARYOP_ASSIGN)
|
||||||
|
{
|
||||||
|
LLVMValueRef value = gencontext_emit_expr(context, expr->binary_expr.right);
|
||||||
|
gencontext_emit_assign(context, expr->binary_expr.left, value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
return gencontext_emit_binary(context, expr, false, binary_op);
|
||||||
|
}
|
||||||
|
|
||||||
|
static LLVMValueRef gencontext_emit_identifier_expr(GenContext *context, Expr *expr)
|
||||||
|
{
|
||||||
|
return LLVMBuildLoad2(context->builder, expr->identifier_expr.decl->var.type->canonical->backend_type,
|
||||||
|
expr->identifier_expr.decl->var.backend_ref, expr->identifier_expr.decl->name.string);
|
||||||
|
}
|
||||||
|
|
||||||
|
LLVMValueRef gencontext_emit_const_expr(GenContext *context, Expr *expr)
|
||||||
|
{
|
||||||
|
LLVMTypeRef type = gencontext_get_llvm_type(context, expr->type->canonical);
|
||||||
|
switch (expr->const_expr.type)
|
||||||
|
{
|
||||||
|
case CONST_INT:
|
||||||
|
return LLVMConstInt(type, expr->const_expr.i, type_is_unsigned(expr->type->canonical) ? false : true);
|
||||||
|
case CONST_FLOAT:
|
||||||
|
return LLVMConstReal(type, (double) expr->const_expr.f);
|
||||||
|
case CONST_NIL:
|
||||||
|
return LLVMConstNull(type);
|
||||||
|
case CONST_BOOL:
|
||||||
|
return LLVMConstInt(type, expr->const_expr.b ? 1 : 0, false);
|
||||||
|
case CONST_STRING:
|
||||||
|
return LLVMConstStringInContext(context->context,
|
||||||
|
expr->const_expr.string.chars,
|
||||||
|
expr->const_expr.string.len,
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
UNREACHABLE
|
||||||
|
}
|
||||||
|
|
||||||
|
LLVMValueRef gencontext_emit_expr(GenContext *context, Expr *expr)
|
||||||
|
{
|
||||||
|
switch (expr->expr_kind)
|
||||||
|
{
|
||||||
|
case EXPR_POISONED:
|
||||||
|
UNREACHABLE
|
||||||
|
case EXPR_UNARY:
|
||||||
|
return gencontext_emit_unary_expr(context, expr);
|
||||||
|
case EXPR_TRY:
|
||||||
|
break;
|
||||||
|
case EXPR_CONST:
|
||||||
|
return gencontext_emit_const_expr(context, expr);
|
||||||
|
case EXPR_BINARY:
|
||||||
|
return gencontext_emit_binary_expr(context, expr);
|
||||||
|
case EXPR_CONDITIONAL:
|
||||||
|
break;
|
||||||
|
case EXPR_POST_UNARY:
|
||||||
|
break;
|
||||||
|
case EXPR_TYPE:
|
||||||
|
break;
|
||||||
|
case EXPR_IDENTIFIER:
|
||||||
|
return gencontext_emit_identifier_expr(context, expr);
|
||||||
|
case EXPR_TYPE_ACCESS:
|
||||||
|
break;
|
||||||
|
case EXPR_CALL:
|
||||||
|
break;
|
||||||
|
case EXPR_SIZEOF:
|
||||||
|
break;
|
||||||
|
case EXPR_SUBSCRIPT:
|
||||||
|
break;
|
||||||
|
case EXPR_ACCESS:
|
||||||
|
break;
|
||||||
|
case EXPR_STRUCT_VALUE:
|
||||||
|
break;
|
||||||
|
case EXPR_STRUCT_INIT_VALUES:
|
||||||
|
break;
|
||||||
|
case EXPR_INITIALIZER_LIST:
|
||||||
|
break;
|
||||||
|
case EXPR_EXPRESSION_LIST:
|
||||||
|
break;
|
||||||
|
case EXPR_CAST:
|
||||||
|
return gencontext_emit_cast_expr(context, expr);
|
||||||
|
}
|
||||||
|
TODO
|
||||||
|
}
|
||||||
187
src/compiler/llvm_codegen_function.c
Normal file
187
src/compiler/llvm_codegen_function.c
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
#include "llvm_codegen_internal.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static char *mangle_name(char *buffer, Decl *decl)
|
||||||
|
{
|
||||||
|
sprintf(buffer, "mangled_name_%*s", decl->name.span.length, decl->name.start);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void gencontext_emit_br(GenContext *context, LLVMBasicBlockRef next_block)
|
||||||
|
{
|
||||||
|
assert(context->current_block);
|
||||||
|
// If it's not used, we can delete the previous block and skip the branch.
|
||||||
|
// Unless it is the entry block or a label target for jumps
|
||||||
|
// These empty blocks will occur when doing branches.
|
||||||
|
// Consider:
|
||||||
|
// while (1)
|
||||||
|
// {
|
||||||
|
// break;
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// Naively we'd output
|
||||||
|
// br label %for.cond - 1st break
|
||||||
|
// br label %for.cond - 2nd break
|
||||||
|
// br label %for.cond - end of scope
|
||||||
|
//
|
||||||
|
// The fix is to introduce a new block after a break:
|
||||||
|
// br label %for.cond
|
||||||
|
// jmp:
|
||||||
|
// br label %for.cond
|
||||||
|
// jmp.1:
|
||||||
|
// br label %for.cond
|
||||||
|
//
|
||||||
|
// But this leaves us with blocks that have no parent.
|
||||||
|
// Consequently we will delete those and realize that
|
||||||
|
// we then have no need for emitting a br.
|
||||||
|
if (!context->current_block_is_target
|
||||||
|
&& !LLVMGetFirstUse(LLVMBasicBlockAsValue(context->current_block)))
|
||||||
|
{
|
||||||
|
LLVMDeleteBasicBlock(context->current_block);
|
||||||
|
context->current_block = NULL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
context->current_block = NULL;
|
||||||
|
LLVMBuildBr(context->builder, next_block);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gencontext_emit_cond_br(GenContext *context, LLVMValueRef value, LLVMBasicBlockRef thenBlock, LLVMBasicBlockRef elseBlock)
|
||||||
|
{
|
||||||
|
assert(context->current_block);
|
||||||
|
LLVMBuildCondBr(context->builder, value, thenBlock, elseBlock);
|
||||||
|
LLVMClearInsertionPosition(context->builder);
|
||||||
|
context->current_block = NULL;
|
||||||
|
context->current_block_is_target = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void gencontext_emit_block(GenContext *context, LLVMBasicBlockRef next_block)
|
||||||
|
{
|
||||||
|
assert(context->current_block == NULL);
|
||||||
|
LLVMAppendExistingBasicBlock(context->function, next_block);
|
||||||
|
LLVMPositionBuilderAtEnd(context->builder, next_block);
|
||||||
|
context->current_block = next_block;
|
||||||
|
context->current_block_is_target = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline void gencontext_emit_parameter(GenContext *context, Decl *decl, unsigned index)
|
||||||
|
{
|
||||||
|
assert(decl->decl_kind == DECL_VAR && decl->var.kind == VARDECL_PARAM);
|
||||||
|
|
||||||
|
// Allocate room on stack and copy.
|
||||||
|
decl->var.backend_ref = gencontext_emit_alloca(context, decl);
|
||||||
|
LLVMBuildStore(context->builder, LLVMGetParam(context->function, index), decl->var.backend_ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gencontext_emit_function_body(GenContext *context, Decl *decl)
|
||||||
|
{
|
||||||
|
assert(decl->func.backend_value);
|
||||||
|
|
||||||
|
LLVMValueRef prev_function = context->function;
|
||||||
|
LLVMBuilderRef prev_builder = context->builder;
|
||||||
|
|
||||||
|
context->function = decl->func.backend_value;
|
||||||
|
|
||||||
|
LLVMBasicBlockRef entry = LLVMAppendBasicBlockInContext(context->context, context->function, "entry");
|
||||||
|
context->current_block = entry;
|
||||||
|
context->current_block_is_target = true;
|
||||||
|
context->builder = LLVMCreateBuilder();
|
||||||
|
LLVMPositionBuilderAtEnd(context->builder, entry);
|
||||||
|
|
||||||
|
LLVMValueRef alloca_point = LLVMBuildAlloca(context->builder, LLVMInt32TypeInContext(context->context), "alloca_point");
|
||||||
|
context->alloca_point = alloca_point;
|
||||||
|
|
||||||
|
// Generate LLVMValueRef's for all parameters, so we can use them as local vars in code
|
||||||
|
VECEACH(decl->func.function_signature.params, i)
|
||||||
|
{
|
||||||
|
gencontext_emit_parameter(context, decl->func.function_signature.params[i], i);
|
||||||
|
}
|
||||||
|
|
||||||
|
VECEACH(decl->func.labels, i)
|
||||||
|
{
|
||||||
|
Ast *label = decl->func.labels[i];
|
||||||
|
label->label_stmt.backend_value = gencontext_create_free_block(context, label->token.string);
|
||||||
|
}
|
||||||
|
|
||||||
|
gencontext_emit_compound_stmt(context, decl->func.body);
|
||||||
|
|
||||||
|
// Insert a return if needed.
|
||||||
|
if (!LLVMGetBasicBlockTerminator(LLVMGetInsertBlock(context->builder)))
|
||||||
|
{
|
||||||
|
assert(decl->func.function_signature.rtype->type_kind == TYPE_VOID);
|
||||||
|
LLVMBuildRetVoid(context->builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
// erase alloca point
|
||||||
|
if (LLVMGetInstructionParent(alloca_point))
|
||||||
|
{
|
||||||
|
context->alloca_point = NULL;
|
||||||
|
LLVMInstructionEraseFromParent(alloca_point);
|
||||||
|
}
|
||||||
|
|
||||||
|
LLVMDisposeBuilder(context->builder);
|
||||||
|
|
||||||
|
context->builder = prev_builder;
|
||||||
|
context->function = prev_function;
|
||||||
|
}
|
||||||
|
|
||||||
|
void gencontext_emit_function_decl(GenContext *context, Decl *decl)
|
||||||
|
{
|
||||||
|
assert(decl->decl_kind == DECL_FUNC);
|
||||||
|
char workbuf[2048] = { '\0' };
|
||||||
|
char *external_name = mangle_name(workbuf, decl);
|
||||||
|
// Resolve function backend type for function.
|
||||||
|
decl->func.backend_value = LLVMAddFunction(context->module, external_name,
|
||||||
|
gencontext_get_llvm_type(context, decl->self_type));
|
||||||
|
|
||||||
|
// Specify appropriate storage class, visibility and call convention
|
||||||
|
// extern functions (linkedited in separately):
|
||||||
|
/*
|
||||||
|
if (glofn->flags & FlagSystem) {
|
||||||
|
LLVMSetFunctionCallConv(glofn->llvmvar, LLVMX86StdcallCallConv);
|
||||||
|
LLVMSetDLLStorageClass(glofn->llvmvar, LLVMDLLImportStorageClass);
|
||||||
|
}*/
|
||||||
|
if (decl->visibility == VISIBLE_LOCAL)
|
||||||
|
{
|
||||||
|
LLVMSetVisibility(decl->func.backend_value, LLVMHiddenVisibility);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context->debug.builder)
|
||||||
|
{
|
||||||
|
LLVMDIFlags flags = LLVMDIFlagZero;
|
||||||
|
if (!decl->func.body) flags |= LLVMDIFlagPrototyped;
|
||||||
|
switch (decl->visibility)
|
||||||
|
{
|
||||||
|
case VISIBLE_LOCAL:
|
||||||
|
flags |= LLVMDIFlagPrivate;
|
||||||
|
break;
|
||||||
|
case VISIBLE_MODULE:
|
||||||
|
flags |= LLVMDIFlagProtected;
|
||||||
|
break;
|
||||||
|
case VISIBLE_PUBLIC:
|
||||||
|
flags |= LLVMDIFlagPublic;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
SourcePosition decl_position = source_file_find_position(decl->name.span.loc);
|
||||||
|
context->debug.function = LLVMDIBuilderCreateFunction(context->debug.builder,
|
||||||
|
context->debug.compile_unit,
|
||||||
|
decl->name.string, decl->name.span.length,
|
||||||
|
decl->name.string, decl->name.span.length,
|
||||||
|
context->debug.file,
|
||||||
|
decl_position.line,
|
||||||
|
decl->self_type->backend_debug_type,
|
||||||
|
decl->visibility == VISIBLE_LOCAL,
|
||||||
|
1,
|
||||||
|
decl_position.line,
|
||||||
|
flags,
|
||||||
|
0);
|
||||||
|
LLVMSetSubprogram(decl->func.backend_value, context->debug.function);
|
||||||
|
}
|
||||||
|
gencontext_emit_function_body(context, decl);
|
||||||
|
}
|
||||||
96
src/compiler/llvm_codegen_internal.h
Normal file
96
src/compiler/llvm_codegen_internal.h
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
#include "compiler_internal.h"
|
||||||
|
#include <llvm-c/Core.h>
|
||||||
|
#include <llvm-c/Analysis.h>
|
||||||
|
#include <llvm-c/ExecutionEngine.h>
|
||||||
|
#include <llvm-c/Target.h>
|
||||||
|
#include <llvm-c/Analysis.h>
|
||||||
|
#include <llvm-c/BitWriter.h>
|
||||||
|
#include <llvm-c/Transforms/Scalar.h>
|
||||||
|
#include <llvm-c/Transforms/IPO.h>
|
||||||
|
#include <llvm-c/Transforms/Utils.h>
|
||||||
|
#include <llvm-c/DebugInfo.h>
|
||||||
|
#include "dwarf.h"
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
LLVMBasicBlockRef continue_block;
|
||||||
|
LLVMBasicBlockRef break_block;
|
||||||
|
LLVMBasicBlockRef next_block;
|
||||||
|
} BreakContinue;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
LLVMDIBuilderRef builder;
|
||||||
|
LLVMMetadataRef file;
|
||||||
|
LLVMMetadataRef compile_unit;
|
||||||
|
LLVMMetadataRef function;
|
||||||
|
SourceRange current_range;
|
||||||
|
LLVMMetadataRef *lexical_block_stack;
|
||||||
|
LLVMMetadataRef inlined_at;
|
||||||
|
} DebugContext;
|
||||||
|
|
||||||
|
#define BREAK_STACK_MAX 256
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
const char *filename;
|
||||||
|
const char *dirname;
|
||||||
|
LLVMModuleRef module;
|
||||||
|
LLVMContextRef context;
|
||||||
|
LLVMValueRef function;
|
||||||
|
LLVMValueRef alloca_point;
|
||||||
|
LLVMBuilderRef builder;
|
||||||
|
LLVMBasicBlockRef current_block;
|
||||||
|
bool current_block_is_target;
|
||||||
|
Decl *cur_code_decl;
|
||||||
|
Decl *cur_func_decl;
|
||||||
|
bool did_call_stack_save;
|
||||||
|
Type *current_return_type;
|
||||||
|
int block_global_unique_count;
|
||||||
|
int ast_alloca_addr_space;
|
||||||
|
BreakContinue return_block;
|
||||||
|
int simple_return_expressions;
|
||||||
|
unsigned pointer_alignment;
|
||||||
|
int return_expressions;
|
||||||
|
Ast **defer_stack;
|
||||||
|
DebugContext debug;
|
||||||
|
Context *ast_context;
|
||||||
|
BreakContinue break_continue_stack[BREAK_STACK_MAX];
|
||||||
|
size_t break_continue_stack_index;
|
||||||
|
|
||||||
|
} GenContext;
|
||||||
|
|
||||||
|
|
||||||
|
void gencontext_begin_module(GenContext *context);
|
||||||
|
void gencontext_end_module(GenContext *context);
|
||||||
|
void gencontext_emit_stmt(GenContext *context, Ast *ast);
|
||||||
|
LLVMValueRef gencontext_emit_expr(GenContext *context, Expr *expr);
|
||||||
|
void gencontext_emit_debug_location(GenContext *context, SourceRange location);
|
||||||
|
LLVMMetadataRef gencontext_create_builtin_debug_type(GenContext *context, Type *builtin_type);
|
||||||
|
LLVMValueRef gencontext_emit_alloca(GenContext *context, Decl *decl);
|
||||||
|
void gencontext_emit_compound_stmt(GenContext *context, Ast *ast);
|
||||||
|
void gencontext_emit_block(GenContext *context, LLVMBasicBlockRef next_block);
|
||||||
|
void gencontext_emit_br(GenContext *context, LLVMBasicBlockRef next_block);
|
||||||
|
void gencontext_emit_cond_br(GenContext *context, LLVMValueRef value, LLVMBasicBlockRef thenBlock, LLVMBasicBlockRef elseBlock);
|
||||||
|
static inline LLVMBasicBlockRef gencontext_create_free_block(GenContext *context, const char *name)
|
||||||
|
{
|
||||||
|
return LLVMCreateBasicBlockInContext(context->context, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gencontext_emit_function_decl(GenContext *context, Decl *decl);
|
||||||
|
LLVMValueRef gencontext_emit_address(GenContext *context, Expr *expr);
|
||||||
|
#define LLVMTYPE(type) gencontext_get_llvm_type(context, type->canonical)
|
||||||
|
|
||||||
|
LLVMTypeRef gencontext_get_llvm_type(GenContext *context, Type *type);
|
||||||
|
|
||||||
|
static inline bool gencontext_use_debug(GenContext *context)
|
||||||
|
{
|
||||||
|
return context->debug.builder != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
121
src/compiler/llvm_codegen_module.c
Normal file
121
src/compiler/llvm_codegen_module.c
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
#include "llvm_codegen_internal.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static inline LLVMTypeRef gencontext_create_basic_llvm_type(GenContext *context, Type *type)
|
||||||
|
{
|
||||||
|
switch (type->type_kind)
|
||||||
|
{
|
||||||
|
case TYPE_BOOL:
|
||||||
|
return LLVMInt1TypeInContext(context->context);
|
||||||
|
case TYPE_I8:
|
||||||
|
case TYPE_U8:
|
||||||
|
return LLVMInt8TypeInContext(context->context);
|
||||||
|
case TYPE_I16:
|
||||||
|
case TYPE_U16:
|
||||||
|
return LLVMInt16TypeInContext(context->context);
|
||||||
|
case TYPE_I32:
|
||||||
|
case TYPE_U32:
|
||||||
|
return LLVMInt32TypeInContext(context->context);
|
||||||
|
case TYPE_I64:
|
||||||
|
case TYPE_U64:
|
||||||
|
return LLVMInt64TypeInContext(context->context);
|
||||||
|
case TYPE_F32:
|
||||||
|
return LLVMFloatTypeInContext(context->context);
|
||||||
|
case TYPE_F64:
|
||||||
|
return LLVMDoubleTypeInContext(context->context);
|
||||||
|
case TYPE_VOID:
|
||||||
|
return LLVMVoidTypeInContext(context->context);
|
||||||
|
default:
|
||||||
|
UNREACHABLE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void gencontext_init_basic_llvm_type(GenContext *context, Type *type)
|
||||||
|
{
|
||||||
|
type->backend_type = gencontext_create_basic_llvm_type(context, type);
|
||||||
|
if (context->debug.builder)
|
||||||
|
{
|
||||||
|
type->backend_debug_type = gencontext_create_builtin_debug_type(context, type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void gencontext_begin_module(GenContext *context)
|
||||||
|
{
|
||||||
|
assert(!context->module && "Expected no module");
|
||||||
|
const char *full_path = context->ast_context->file->full_path;
|
||||||
|
char *mangled_module_name = strformat("module:%s", context->ast_context->module->name);
|
||||||
|
context->module = LLVMModuleCreateWithNameInContext(mangled_module_name, context->context);
|
||||||
|
LLVMSetModuleDataLayout(context->module, target_data_layout());
|
||||||
|
LLVMSetSourceFileName(context->module, full_path, strlen(context->ast_context->file->full_path));
|
||||||
|
|
||||||
|
LLVMSetTarget(context->module, build_options.target);
|
||||||
|
if (build_options.debug_info)
|
||||||
|
{
|
||||||
|
const char *filename = context->ast_context->file->name;
|
||||||
|
const char *dir_path = context->ast_context->file->dir_path;
|
||||||
|
context->debug.builder = LLVMCreateDIBuilder(context->module);
|
||||||
|
context->debug.file = LLVMDIBuilderCreateFile(context->debug.builder, filename, strlen(filename), dir_path, strlen(dir_path));
|
||||||
|
|
||||||
|
bool is_optimized = false;
|
||||||
|
const char *dwarf_flags = "";
|
||||||
|
unsigned runtime_version = 1;
|
||||||
|
LLVMDWARFEmissionKind emission_kind = LLVMDWARFEmissionFull;
|
||||||
|
context->debug.compile_unit = LLVMDIBuilderCreateCompileUnit(context->debug.builder, LLVMDWARFSourceLanguageC,
|
||||||
|
context->debug.file, DWARF_PRODUCER_NAME,
|
||||||
|
strlen(DWARF_PRODUCER_NAME), is_optimized,
|
||||||
|
dwarf_flags, strlen(dwarf_flags),
|
||||||
|
runtime_version, "" /* split name */, 0 /* len */,
|
||||||
|
emission_kind, /* dwo */0, /* inlining */0,
|
||||||
|
/* debug for profiling */0);
|
||||||
|
}
|
||||||
|
// Setup all types. Not thread-safe, but at this point in time we can assume a single context.
|
||||||
|
// We need to remove the context from the cache after this.
|
||||||
|
// This would seem to indicate that we should change Type / actual type.
|
||||||
|
gencontext_init_basic_llvm_type(context, type_char);
|
||||||
|
gencontext_init_basic_llvm_type(context, type_byte);
|
||||||
|
gencontext_init_basic_llvm_type(context, type_short);
|
||||||
|
gencontext_init_basic_llvm_type(context, type_ushort);
|
||||||
|
gencontext_init_basic_llvm_type(context, type_int);
|
||||||
|
gencontext_init_basic_llvm_type(context, type_uint);
|
||||||
|
gencontext_init_basic_llvm_type(context, type_long);
|
||||||
|
gencontext_init_basic_llvm_type(context, type_ulong);
|
||||||
|
gencontext_init_basic_llvm_type(context, type_float);
|
||||||
|
gencontext_init_basic_llvm_type(context, type_double);
|
||||||
|
gencontext_init_basic_llvm_type(context, type_void);
|
||||||
|
gencontext_init_basic_llvm_type(context, type_bool);
|
||||||
|
|
||||||
|
context->pointer_alignment = LLVMPointerSizeForAS(target_data_layout(), 0);
|
||||||
|
|
||||||
|
context->block_global_unique_count = 0;
|
||||||
|
context->ast_alloca_addr_space = target_alloca_addr_space();
|
||||||
|
|
||||||
|
/*
|
||||||
|
SizeSizeInBytes =
|
||||||
|
C.toCharUnitsFromBits(C.getTargetInfo().getMaxPointerWidth()).getQuantity();
|
||||||
|
IntAlignInBytes =
|
||||||
|
C.toCharUnitsFromBits(C.getTargetInfo().getIntAlign()).getQuantity();
|
||||||
|
IntTy = llvm::IntegerType::get(LLVMContext, C.getTargetInfo().getIntWidth());
|
||||||
|
IntPtrTy = llvm::IntegerType::get(LLVMContext,
|
||||||
|
C.getTargetInfo().getMaxPointerWidth());
|
||||||
|
Int8PtrTy = Int8Ty->getPointerTo(0);
|
||||||
|
Int8PtrPtrTy = Int8PtrTy->getPointerTo(0);
|
||||||
|
AllocaInt8PtrTy = Int8Ty->getPointerTo(
|
||||||
|
M.getDataLayout().getAllocaAddrSpace());
|
||||||
|
ASTAllocaAddressSpace = getTargetCodeGenInfo().getASTAllocaAddressSpace();
|
||||||
|
*/
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void gencontext_end_module(GenContext *context)
|
||||||
|
{
|
||||||
|
if (context->debug.builder)
|
||||||
|
{
|
||||||
|
LLVMDIBuilderFinalize(context->debug.builder);
|
||||||
|
}
|
||||||
|
LLVMDisposeModule(context->module);
|
||||||
|
}
|
||||||
471
src/compiler/llvm_codegen_stmt.c
Normal file
471
src/compiler/llvm_codegen_stmt.c
Normal file
@@ -0,0 +1,471 @@
|
|||||||
|
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
#include "llvm_codegen_internal.h"
|
||||||
|
|
||||||
|
static void gencontext_pop_break_continue(GenContext *context);
|
||||||
|
static void
|
||||||
|
gencontext_push_break_continue(GenContext *context, LLVMBasicBlockRef break_block, LLVMBasicBlockRef continue_block,
|
||||||
|
LLVMBasicBlockRef next_block);
|
||||||
|
|
||||||
|
void gencontext_emit_compound_stmt(GenContext *context, Ast *ast)
|
||||||
|
{
|
||||||
|
assert(ast->ast_kind == AST_COMPOUND_STMT);
|
||||||
|
VECEACH(ast->compound_stmt.stmts, i)
|
||||||
|
{
|
||||||
|
gencontext_emit_stmt(context, ast->compound_stmt.stmts[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void gencontext_emit_stmt_list(GenContext *context, Ast *ast)
|
||||||
|
{
|
||||||
|
assert(ast->ast_kind == AST_STMT_LIST);
|
||||||
|
VECEACH(ast->stmt_list, i)
|
||||||
|
{
|
||||||
|
gencontext_emit_stmt(context, ast->stmt_list[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void gencontext_emit_return(GenContext *context, Ast *ast)
|
||||||
|
{
|
||||||
|
if (!ast->return_stmt.expr)
|
||||||
|
{
|
||||||
|
LLVMBuildRetVoid(context->builder);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
LLVMValueRef returnValue = gencontext_emit_expr(context, ast->return_stmt.expr);
|
||||||
|
LLVMBuildRet(context->builder, returnValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline LLVMValueRef gencontext_emit_cond(GenContext *context, Ast *ast)
|
||||||
|
{
|
||||||
|
assert(ast->ast_kind == AST_COND_STMT);
|
||||||
|
VECEACH(ast->cond_stmt.stmts, i)
|
||||||
|
{
|
||||||
|
gencontext_emit_stmt(context, ast->cond_stmt.stmts[i]);
|
||||||
|
}
|
||||||
|
return gencontext_emit_expr(context, ast->cond_stmt.expr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static LLVMValueRef gencontext_emit_decl(GenContext *context, Ast *ast)
|
||||||
|
{
|
||||||
|
Decl *decl = ast->declare_stmt;
|
||||||
|
|
||||||
|
decl->var.backend_ref = gencontext_emit_alloca(context, decl);
|
||||||
|
// TODO NRVO
|
||||||
|
// TODO debug info
|
||||||
|
/*
|
||||||
|
if (EmitDebugInfo && HaveInsertPoint()) {
|
||||||
|
Address DebugAddr = address;
|
||||||
|
bool UsePointerValue = NRVO && ReturnValuePointer.isValid();
|
||||||
|
DI->setLocation(D.getLocation());
|
||||||
|
|
||||||
|
// If NRVO, use a pointer to the return address.
|
||||||
|
if (UsePointerValue)
|
||||||
|
DebugAddr = ReturnValuePointer;
|
||||||
|
|
||||||
|
(void)DI->EmitDeclareOfAutoVariable(&D, DebugAddr.getPointer(), Builder,
|
||||||
|
UsePointerValue);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
if (decl->var.init_expr)
|
||||||
|
{
|
||||||
|
LLVMValueRef value = gencontext_emit_expr(context, decl->var.init_expr);
|
||||||
|
LLVMBuildStore(context->builder, value, decl->var.backend_ref);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
return decl->var.backend_ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void gencontext_emit_if(GenContext *context, Ast *ast)
|
||||||
|
{
|
||||||
|
// In the case of something like if (foo = 1, int bar = 2; b > 0) { ... }
|
||||||
|
// Let's first emit foo = 1, int bar = 2
|
||||||
|
// IMPROVE Consider whether these should be lowered or not.
|
||||||
|
Ast **stmts = ast->if_stmt.cond->cond_stmt.stmts;
|
||||||
|
VECEACH(stmts, i)
|
||||||
|
{
|
||||||
|
gencontext_emit_stmt(context, stmts[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need at least the exit block and the "then" block.
|
||||||
|
LLVMBasicBlockRef exit_block = LLVMCreateBasicBlockInContext(context->context, "if.exit");
|
||||||
|
LLVMBasicBlockRef then_block = LLVMCreateBasicBlockInContext(context->context, "if.then");
|
||||||
|
LLVMBasicBlockRef else_block = NULL;
|
||||||
|
|
||||||
|
// We have an optional else block.
|
||||||
|
if (ast->if_stmt.else_body)
|
||||||
|
{
|
||||||
|
else_block = LLVMCreateBasicBlockInContext(context->context, "if.else");
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(ast->if_stmt.cond->cond_stmt.expr->type->type_kind == TYPE_BOOL);
|
||||||
|
|
||||||
|
// Output boolean value and switch.
|
||||||
|
LLVMValueRef value = gencontext_emit_expr(context, ast->if_stmt.cond->cond_stmt.expr);
|
||||||
|
gencontext_emit_cond_br(context, value, then_block, else_block ? else_block : exit_block);
|
||||||
|
|
||||||
|
// Emit the 'then' code.
|
||||||
|
gencontext_emit_block(context, then_block);
|
||||||
|
gencontext_emit_stmt(context, ast->if_stmt.then_body);
|
||||||
|
|
||||||
|
// Jump to exit.
|
||||||
|
gencontext_emit_br(context, exit_block);
|
||||||
|
|
||||||
|
// Emit the 'else' branch if present.
|
||||||
|
if (else_block)
|
||||||
|
{
|
||||||
|
gencontext_emit_block(context, else_block);
|
||||||
|
gencontext_emit_stmt(context, ast->if_stmt.else_body);
|
||||||
|
gencontext_emit_br(context, exit_block);
|
||||||
|
}
|
||||||
|
|
||||||
|
// And now we just emit the exit block.
|
||||||
|
gencontext_emit_block(context, exit_block);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void gencontext_push_next(GenContext *context, LLVMBasicBlockRef nextBlock)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
static void
|
||||||
|
gencontext_push_break_continue(GenContext *context, LLVMBasicBlockRef break_block, LLVMBasicBlockRef continue_block,
|
||||||
|
LLVMBasicBlockRef next_block)
|
||||||
|
{
|
||||||
|
size_t index = context->break_continue_stack_index++;
|
||||||
|
if (index == BREAK_STACK_MAX - 1)
|
||||||
|
{
|
||||||
|
error_exit("Exhausted break/continue stack - exceeded %d entries.", BREAK_STACK_MAX);
|
||||||
|
}
|
||||||
|
if (!index)
|
||||||
|
{
|
||||||
|
context->break_continue_stack[index].continue_block = continue_block;
|
||||||
|
context->break_continue_stack[index].break_block = break_block;
|
||||||
|
context->break_continue_stack[index].next_block = next_block;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
context->break_continue_stack[index].continue_block = continue_block ? continue_block : context->break_continue_stack[index - 1].continue_block;
|
||||||
|
context->break_continue_stack[index].next_block = next_block ? next_block : context->break_continue_stack[index - 1].next_block;
|
||||||
|
context->break_continue_stack[index].break_block = break_block ? break_block : context->break_continue_stack[index - 1].break_block;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gencontext_pop_break_continue(GenContext *context)
|
||||||
|
{
|
||||||
|
assert(context->break_continue_stack_index);
|
||||||
|
context->break_continue_stack_index--;
|
||||||
|
}
|
||||||
|
|
||||||
|
void gencontext_emit_for_stmt(GenContext *context, Ast *ast)
|
||||||
|
{
|
||||||
|
// First, emit all inits.
|
||||||
|
|
||||||
|
Ast **init_stmts = ast->for_stmt.init;
|
||||||
|
|
||||||
|
VECEACH(init_stmts, i)
|
||||||
|
{
|
||||||
|
gencontext_emit_stmt(context, init_stmts[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We have 3 optional parts, which makes this code bit complicated.
|
||||||
|
LLVMBasicBlockRef exit_block = gencontext_create_free_block(context, "for.exit");
|
||||||
|
LLVMBasicBlockRef inc_block = ast->for_stmt.incr ? gencontext_create_free_block(context, "for.inc") : NULL;
|
||||||
|
LLVMBasicBlockRef body_block = ast->for_stmt.body->compound_stmt.stmts ? gencontext_create_free_block(context, "for.body") : NULL;
|
||||||
|
LLVMBasicBlockRef cond_block = ast->for_stmt.cond ? gencontext_create_free_block(context, "for.cond") : NULL;
|
||||||
|
|
||||||
|
// A loop must either have a body or an inc.
|
||||||
|
// This type of for loop is forbidden:
|
||||||
|
// for (;;);
|
||||||
|
assert(cond_block || inc_block || body_block && "For has no body, no inc and no cond.");
|
||||||
|
|
||||||
|
// Break is simple it always jumps out.
|
||||||
|
// For continue:
|
||||||
|
// 1. If there is inc, jump to the condition
|
||||||
|
// 2. If there is no condition, jump to the body.
|
||||||
|
gencontext_push_break_continue(context,
|
||||||
|
exit_block,
|
||||||
|
inc_block ? inc_block : (cond_block ? cond_block : body_block),
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
LLVMValueRef value = NULL;
|
||||||
|
|
||||||
|
if (cond_block)
|
||||||
|
{
|
||||||
|
// Emit cond
|
||||||
|
gencontext_emit_br(context, cond_block);
|
||||||
|
gencontext_emit_block(context, cond_block);
|
||||||
|
value = gencontext_emit_expr(context, ast->for_stmt.cond->cond_stmt.expr);
|
||||||
|
// If we have a body, conditionally jump to it.
|
||||||
|
if (body_block)
|
||||||
|
{
|
||||||
|
gencontext_emit_cond_br(context, value, body_block, exit_block);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Otherwise jump to inc or cond depending on what's available.
|
||||||
|
gencontext_emit_cond_br(context, value, inc_block ? inc_block : cond_block, exit_block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (body_block)
|
||||||
|
{
|
||||||
|
if (!cond_block)
|
||||||
|
{
|
||||||
|
// We don't have a cond, so we need to unconditionally jump here.
|
||||||
|
gencontext_emit_br(context, body_block);
|
||||||
|
}
|
||||||
|
gencontext_emit_block(context, body_block);
|
||||||
|
gencontext_emit_stmt(context, ast->for_stmt.body);
|
||||||
|
// IMPROVE handle continue/break.
|
||||||
|
if (inc_block)
|
||||||
|
{
|
||||||
|
gencontext_emit_br(context, inc_block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inc_block)
|
||||||
|
{
|
||||||
|
// Emit the block
|
||||||
|
gencontext_emit_block(context, inc_block);
|
||||||
|
gencontext_emit_stmt(context, ast->for_stmt.incr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop back.
|
||||||
|
gencontext_emit_br(context, cond_block ? cond_block : (body_block ? body_block : inc_block));
|
||||||
|
|
||||||
|
// And insert exit block
|
||||||
|
gencontext_emit_block(context, exit_block);
|
||||||
|
gencontext_pop_break_continue(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gencontext_emit_do_stmt(GenContext *context, Ast *ast)
|
||||||
|
{
|
||||||
|
LLVMBasicBlockRef exit_block = gencontext_create_free_block(context, "do.exit");
|
||||||
|
LLVMBasicBlockRef cond_block = ast->do_stmt.expr ? gencontext_create_free_block(context, "do.cond") : NULL;
|
||||||
|
LLVMBasicBlockRef body_block = ast->do_stmt.body ? gencontext_create_free_block(context, "do.body") : NULL;
|
||||||
|
|
||||||
|
// A loop must either have a body or an inc.
|
||||||
|
// This type do-while for loop is forbidden:
|
||||||
|
// do { } while (1);
|
||||||
|
assert(cond_block || body_block && "Do has no body and no cond.");
|
||||||
|
|
||||||
|
// Break is simple it always jumps out.
|
||||||
|
// For continue: if there is no condition, jump to the body.
|
||||||
|
gencontext_push_break_continue(context, exit_block, cond_block ? cond_block : body_block, NULL);
|
||||||
|
|
||||||
|
if (body_block)
|
||||||
|
{
|
||||||
|
// Emit the body
|
||||||
|
gencontext_emit_br(context, body_block);
|
||||||
|
gencontext_emit_block(context, body_block);
|
||||||
|
gencontext_emit_stmt(context, ast->do_stmt.body);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cond_block)
|
||||||
|
{
|
||||||
|
gencontext_emit_br(context, cond_block);
|
||||||
|
gencontext_emit_block(context, cond_block);
|
||||||
|
LLVMValueRef value = gencontext_emit_expr(context, ast->do_stmt.expr);
|
||||||
|
gencontext_emit_cond_br(context, value, body_block ? body_block : cond_block, exit_block);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Branch to the beginning of the block
|
||||||
|
gencontext_emit_br(context, body_block);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emit the exit block.
|
||||||
|
gencontext_emit_block(context, exit_block);
|
||||||
|
|
||||||
|
gencontext_pop_break_continue(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gencontext_emit_jmp(GenContext *context, LLVMBasicBlockRef block)
|
||||||
|
{
|
||||||
|
gencontext_emit_br(context, block);
|
||||||
|
LLVMBasicBlockRef post_jump_block = gencontext_create_free_block(context, "jmp");
|
||||||
|
gencontext_emit_block(context, post_jump_block);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gencontext_emit_label(GenContext *context, Ast *ast)
|
||||||
|
{
|
||||||
|
gencontext_emit_br(context, ast->label_stmt.backend_value);
|
||||||
|
gencontext_emit_block(context, ast->label_stmt.backend_value);
|
||||||
|
context->current_block_is_target = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void gencontext_emit_switch(GenContext *context, Ast *ast)
|
||||||
|
{
|
||||||
|
LLVMValueRef switch_value = gencontext_emit_cond(context, ast->switch_stmt.cond);
|
||||||
|
|
||||||
|
size_t cases = vec_size(ast->switch_stmt.cases);
|
||||||
|
if (!cases)
|
||||||
|
{
|
||||||
|
// No body or default is empty, just exit after the value.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ast *default_case = NULL;
|
||||||
|
VECEACH(ast->switch_stmt.cases, i)
|
||||||
|
{
|
||||||
|
Ast *case_stmt = ast->switch_stmt.cases[i];
|
||||||
|
if (case_stmt->case_stmt.value_type == CASE_VALUE_DEFAULT)
|
||||||
|
{
|
||||||
|
if (case_stmt->case_stmt.block)
|
||||||
|
{
|
||||||
|
case_stmt->case_stmt.backend_value = gencontext_create_free_block(context, "switch.default");
|
||||||
|
}
|
||||||
|
default_case = case_stmt;
|
||||||
|
}
|
||||||
|
else if (case_stmt->case_stmt.block)
|
||||||
|
{
|
||||||
|
case_stmt->case_stmt.backend_value = gencontext_create_free_block(context, "switch.case");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LLVMBasicBlockRef exit_block = gencontext_create_free_block(context, "switch.exit");
|
||||||
|
|
||||||
|
// We will now treat the fallthrough cases:
|
||||||
|
// switch (i)
|
||||||
|
// {
|
||||||
|
// case 1:
|
||||||
|
// case 2:
|
||||||
|
// do_something();
|
||||||
|
// default:
|
||||||
|
// }
|
||||||
|
VECEACH(ast->switch_stmt.cases, i)
|
||||||
|
{
|
||||||
|
Ast *case_stmt = ast->switch_stmt.cases[i];
|
||||||
|
if (case_stmt->case_stmt.backend_value != NULL) continue;
|
||||||
|
|
||||||
|
// Look forward for a block
|
||||||
|
for (size_t j = i + 1; j < cases; j++)
|
||||||
|
{
|
||||||
|
Ast *other_case = ast->switch_stmt.cases[j];
|
||||||
|
if (other_case->case_stmt.backend_value != NULL)
|
||||||
|
{
|
||||||
|
case_stmt->case_stmt.backend_value = other_case->case_stmt.backend_value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// No block found? Then the block is the exit block.
|
||||||
|
if (!case_stmt->case_stmt.backend_value)
|
||||||
|
{
|
||||||
|
case_stmt->case_stmt.backend_value = exit_block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gencontext_push_break_continue(context, exit_block, NULL, NULL);
|
||||||
|
|
||||||
|
LLVMValueRef switch_stmt = LLVMBuildSwitch(context->builder, switch_value, default_case ? default_case->case_stmt.backend_value : exit_block, cases);
|
||||||
|
context->current_block = NULL;
|
||||||
|
VECEACH(ast->switch_stmt.cases, i)
|
||||||
|
{
|
||||||
|
Ast *case_stmt = ast->switch_stmt.cases[i];
|
||||||
|
LLVMBasicBlockRef block = case_stmt->case_stmt.backend_value;
|
||||||
|
if (case_stmt != default_case)
|
||||||
|
{
|
||||||
|
LLVMValueRef case_value = LLVMConstInt(LLVMTypeOf(switch_value), case_stmt->case_stmt.val, case_stmt->case_stmt.value_type == CASE_VALUE_INT);
|
||||||
|
LLVMAddCase(switch_stmt, case_value, block);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip fallthroughs.
|
||||||
|
if (!case_stmt->case_stmt.block) continue;
|
||||||
|
|
||||||
|
gencontext_emit_block(context, block);
|
||||||
|
// IMPORTANT!
|
||||||
|
context->current_block_is_target = true;
|
||||||
|
gencontext_push_break_continue(context, NULL, NULL, i < cases - 1 ? ast->switch_stmt.cases[i + 1]->case_stmt.backend_value : exit_block);
|
||||||
|
gencontext_emit_stmt(context, case_stmt->case_stmt.block);
|
||||||
|
gencontext_pop_break_continue(context);
|
||||||
|
gencontext_emit_br(context, exit_block);
|
||||||
|
}
|
||||||
|
gencontext_pop_break_continue(context);
|
||||||
|
gencontext_emit_block(context, exit_block);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void gencontext_emit_stmt(GenContext *context, Ast *ast)
|
||||||
|
{
|
||||||
|
switch (ast->ast_kind)
|
||||||
|
{
|
||||||
|
case AST_POISONED:
|
||||||
|
UNREACHABLE
|
||||||
|
case AST_EXPR_STMT:
|
||||||
|
gencontext_emit_expr(context, ast->expr_stmt);
|
||||||
|
break;
|
||||||
|
case AST_DECLARE_STMT:
|
||||||
|
gencontext_emit_decl(context, ast);
|
||||||
|
break;
|
||||||
|
case AST_BREAK_STMT:
|
||||||
|
assert(context->break_continue_stack_index);
|
||||||
|
gencontext_emit_jmp(context, context->break_continue_stack[context->break_continue_stack_index - 1].break_block);
|
||||||
|
break;
|
||||||
|
case AST_CONTINUE_STMT:
|
||||||
|
assert(context->break_continue_stack_index);
|
||||||
|
gencontext_emit_jmp(context, context->break_continue_stack[context->break_continue_stack_index - 1].continue_block);
|
||||||
|
break;
|
||||||
|
case AST_IF_STMT:
|
||||||
|
gencontext_emit_if(context, ast);
|
||||||
|
break;
|
||||||
|
case AST_RETURN_STMT:
|
||||||
|
gencontext_emit_return(context, ast);
|
||||||
|
break;
|
||||||
|
case AST_COMPOUND_STMT:
|
||||||
|
gencontext_emit_compound_stmt(context, ast);
|
||||||
|
break;
|
||||||
|
case AST_FOR_STMT:
|
||||||
|
gencontext_emit_for_stmt(context, ast);
|
||||||
|
break;
|
||||||
|
case AST_DO_STMT:
|
||||||
|
gencontext_emit_do_stmt(context, ast);
|
||||||
|
break;
|
||||||
|
case AST_NEXT_STMT:
|
||||||
|
assert(context->break_continue_stack_index);
|
||||||
|
gencontext_emit_jmp(context, context->break_continue_stack[context->break_continue_stack_index - 1].next_block);
|
||||||
|
break;
|
||||||
|
case AST_NOP_STMT:
|
||||||
|
break;
|
||||||
|
case AST_WHILE_STMT:
|
||||||
|
case AST_CATCH_STMT:
|
||||||
|
case AST_DEFER_STMT:
|
||||||
|
case AST_TRY_STMT:
|
||||||
|
case AST_THROW_STMT:
|
||||||
|
// Should have been lowered.
|
||||||
|
UNREACHABLE
|
||||||
|
case AST_ASM_STMT:
|
||||||
|
TODO
|
||||||
|
case AST_ATTRIBUTE:
|
||||||
|
case AST_COND_STMT:
|
||||||
|
case AST_CT_IF_STMT:
|
||||||
|
case AST_CT_ELIF_STMT:
|
||||||
|
case AST_CT_ELSE_STMT:
|
||||||
|
case AST_CT_FOR_STMT:
|
||||||
|
case AST_CT_SWITCH_STMT:
|
||||||
|
case AST_CT_DEFAULT_STMT:
|
||||||
|
case AST_CT_CASE_STMT:
|
||||||
|
case AST_GENERIC_CASE_STMT:
|
||||||
|
case AST_GENERIC_DEFAULT_STMT:
|
||||||
|
UNREACHABLE
|
||||||
|
case AST_LABEL:
|
||||||
|
gencontext_emit_label(context, ast);
|
||||||
|
break;
|
||||||
|
case AST_GOTO_STMT:
|
||||||
|
gencontext_emit_jmp(context, ast->goto_stmt.label->label_stmt.backend_value);
|
||||||
|
break;
|
||||||
|
case AST_STMT_LIST:
|
||||||
|
gencontext_emit_stmt_list(context, ast);
|
||||||
|
break;
|
||||||
|
case AST_SWITCH_STMT:
|
||||||
|
gencontext_emit_switch(context, ast);
|
||||||
|
break;
|
||||||
|
case AST_CASE_STMT:
|
||||||
|
case AST_DEFAULT_STMT:
|
||||||
|
TODO
|
||||||
|
case AST_VOLATILE_STMT:
|
||||||
|
TODO
|
||||||
|
}
|
||||||
|
}
|
||||||
188
src/compiler/llvm_codegen_type.c
Normal file
188
src/compiler/llvm_codegen_type.c
Normal file
@@ -0,0 +1,188 @@
|
|||||||
|
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
#include "llvm_codegen_internal.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static inline LLVMTypeRef gencontext_create_llvm_type_from_decl(GenContext *context, Decl *decl)
|
||||||
|
{
|
||||||
|
static LLVMTypeRef params[512];
|
||||||
|
static LLVMMetadataRef debug_params[512];
|
||||||
|
switch (decl->decl_kind)
|
||||||
|
{
|
||||||
|
case DECL_ATTRIBUTE:
|
||||||
|
case DECL_POISONED:
|
||||||
|
case DECL_GENERIC:
|
||||||
|
case DECL_MULTI_DECL:
|
||||||
|
case DECL_MACRO:
|
||||||
|
case DECL_CT_IF:
|
||||||
|
case DECL_CT_ELSE:
|
||||||
|
case DECL_CT_ELIF:
|
||||||
|
UNREACHABLE;
|
||||||
|
case DECL_FUNC:
|
||||||
|
{
|
||||||
|
VECEACH(decl->func.function_signature.params, i)
|
||||||
|
{
|
||||||
|
Type *param_type = decl->func.function_signature.params[i]->var.type;
|
||||||
|
params[i] = gencontext_get_llvm_type(context, param_type);
|
||||||
|
if (build_options.debug_info)
|
||||||
|
{
|
||||||
|
assert(param_type->backend_debug_type);
|
||||||
|
debug_params[i + 1] = param_type->backend_debug_type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unsigned param_size = vec_size(decl->func.function_signature.params);
|
||||||
|
if (gencontext_use_debug(context))
|
||||||
|
{
|
||||||
|
gencontext_get_llvm_type(context, decl->func.function_signature.rtype);
|
||||||
|
debug_params[0] = decl->func.function_signature.rtype->backend_debug_type;
|
||||||
|
LLVMMetadataRef function_type = LLVMDIBuilderCreateSubroutineType(context->debug.builder,
|
||||||
|
context->debug.file,
|
||||||
|
debug_params, param_size + 1,
|
||||||
|
/** TODO **/ 0);
|
||||||
|
decl->self_type->backend_debug_type = function_type;
|
||||||
|
}
|
||||||
|
return LLVMFunctionType(gencontext_get_llvm_type(context, decl->func.function_signature.rtype),
|
||||||
|
params,
|
||||||
|
param_size,
|
||||||
|
decl->func.function_signature.variadic);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
case DECL_VAR:
|
||||||
|
break;
|
||||||
|
case DECL_ENUM_CONSTANT:
|
||||||
|
break;
|
||||||
|
case DECL_TYPEDEF:
|
||||||
|
break;
|
||||||
|
case DECL_STRUCT:
|
||||||
|
{
|
||||||
|
LLVMTypeRef *types = NULL;
|
||||||
|
VECEACH(decl->strukt.members, i)
|
||||||
|
{
|
||||||
|
VECADD(types, gencontext_get_llvm_type(context, decl->strukt.members[i]->var.type));
|
||||||
|
}
|
||||||
|
LLVMTypeRef type = LLVMStructCreateNamed(context->context, decl->name.string);
|
||||||
|
LLVMStructSetBody(type, types, vec_size(types), decl->is_packed);
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
case DECL_UNION:
|
||||||
|
break;
|
||||||
|
case DECL_ENUM:
|
||||||
|
break;
|
||||||
|
case DECL_ERROR:
|
||||||
|
break;
|
||||||
|
case DECL_ERROR_CONSTANT:
|
||||||
|
break;
|
||||||
|
case DECL_ARRAY_VALUE:
|
||||||
|
break;
|
||||||
|
case DECL_IMPORT:
|
||||||
|
UNREACHABLE
|
||||||
|
}
|
||||||
|
UNREACHABLE
|
||||||
|
}
|
||||||
|
static inline LLVMTypeRef gencontext_create_llvm_type_from_ptr(GenContext *context, Type *type)
|
||||||
|
{
|
||||||
|
LLVMTypeRef base_llvm_type = gencontext_get_llvm_type(context, type->base);
|
||||||
|
|
||||||
|
if (gencontext_use_debug(context))
|
||||||
|
{
|
||||||
|
type->backend_debug_type = LLVMDIBuilderCreatePointerType(context->debug.builder, type->base->backend_debug_type, type_size(type->canonical->base), 0, 0, /* TODO */ "TODO", 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type->canonical != type)
|
||||||
|
{
|
||||||
|
return type->backend_type = gencontext_get_llvm_type(context, type->canonical);
|
||||||
|
}
|
||||||
|
|
||||||
|
return type->backend_type = LLVMPointerType(base_llvm_type, /** TODO **/0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline LLVMTypeRef gencontext_create_llvm_type_from_array(GenContext *context, Type *type)
|
||||||
|
{
|
||||||
|
LLVMTypeRef base_llvm_type = gencontext_get_llvm_type(context, type->base);
|
||||||
|
|
||||||
|
if (gencontext_use_debug(context))
|
||||||
|
{
|
||||||
|
LLVMMetadataRef *ranges = NULL;
|
||||||
|
Type *current_type = type;
|
||||||
|
while (current_type->canonical->type_kind == TYPE_ARRAY)
|
||||||
|
{
|
||||||
|
VECADD(ranges, LLVMDIBuilderGetOrCreateSubrange(context->debug.builder, 0, current_type->canonical->len));
|
||||||
|
current_type = current_type->canonical->base;
|
||||||
|
}
|
||||||
|
type->backend_debug_type = LLVMDIBuilderCreateArrayType(
|
||||||
|
context->debug.builder,
|
||||||
|
type->len,
|
||||||
|
0 /* ALIGN */,
|
||||||
|
type->base->backend_debug_type,
|
||||||
|
ranges, vec_size(ranges));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type->canonical != type)
|
||||||
|
{
|
||||||
|
return type->backend_type = gencontext_get_llvm_type(context, type->canonical);
|
||||||
|
}
|
||||||
|
|
||||||
|
return type->backend_type = LLVMPointerType(base_llvm_type, /** TODO **/0);
|
||||||
|
}
|
||||||
|
|
||||||
|
LLVMTypeRef gencontext_create_llvm_func_type(GenContext *context, Type *type)
|
||||||
|
{
|
||||||
|
LLVMTypeRef *params = NULL;
|
||||||
|
FunctionSignature *signature = type->func.signature;
|
||||||
|
// TODO throws
|
||||||
|
if (vec_size(signature->params))
|
||||||
|
{
|
||||||
|
params = malloc_arena(sizeof(LLVMTypeRef) * vec_size(signature->params));
|
||||||
|
VECEACH(signature->params, i)
|
||||||
|
{
|
||||||
|
params[i] = gencontext_get_llvm_type(context, signature->params[i]->var.type->canonical);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return LLVMFunctionType(
|
||||||
|
gencontext_get_llvm_type(context, type->func.signature->rtype),
|
||||||
|
params, vec_size(signature->params), signature->variadic);
|
||||||
|
}
|
||||||
|
|
||||||
|
LLVMTypeRef gencontext_get_llvm_type(GenContext *context, Type *type)
|
||||||
|
{
|
||||||
|
if (type->backend_type) return type->backend_type;
|
||||||
|
switch (type->type_kind)
|
||||||
|
{
|
||||||
|
case TYPE_USER_DEFINED:
|
||||||
|
return type->backend_type = gencontext_create_llvm_type_from_decl(context, type->decl);
|
||||||
|
case TYPE_FUNC:
|
||||||
|
return type->backend_type = gencontext_create_llvm_func_type(context, type);
|
||||||
|
case TYPE_VOID:
|
||||||
|
case TYPE_F64:
|
||||||
|
case TYPE_F32:
|
||||||
|
case TYPE_U64:
|
||||||
|
case TYPE_POISONED:
|
||||||
|
case TYPE_BOOL:
|
||||||
|
case TYPE_I8:
|
||||||
|
case TYPE_I16:
|
||||||
|
case TYPE_I32:
|
||||||
|
case TYPE_I64:
|
||||||
|
case TYPE_IXX:
|
||||||
|
case TYPE_U8:
|
||||||
|
case TYPE_U16:
|
||||||
|
case TYPE_U32:
|
||||||
|
case TYPE_UXX:
|
||||||
|
case TYPE_FXX:
|
||||||
|
case TYPE_EXPRESSION:
|
||||||
|
case TYPE_INC_ARRAY:
|
||||||
|
UNREACHABLE;
|
||||||
|
case TYPE_POINTER:
|
||||||
|
return type->backend_type = gencontext_create_llvm_type_from_ptr(context, type);
|
||||||
|
case TYPE_STRING:
|
||||||
|
TODO;
|
||||||
|
case TYPE_ARRAY:
|
||||||
|
return type->backend_type = gencontext_create_llvm_type_from_array(context, type);
|
||||||
|
case TYPE_VARARRAY:
|
||||||
|
TODO
|
||||||
|
}
|
||||||
|
UNREACHABLE;
|
||||||
|
}
|
||||||
@@ -2229,7 +2229,7 @@ static inline bool parse_func_typedef(Decl *decl, Visibility visibility)
|
|||||||
|
|
||||||
static inline Decl *parse_typedef_declaration(Visibility visibility)
|
static inline Decl *parse_typedef_declaration(Visibility visibility)
|
||||||
{
|
{
|
||||||
Decl *decl = decl_new(DECL_TYPEDEF, tok, visibility);
|
Decl *decl = decl_new_user_defined_type(tok, DECL_TYPEDEF, visibility);
|
||||||
advance_and_verify(TOKEN_TYPEDEF);
|
advance_and_verify(TOKEN_TYPEDEF);
|
||||||
if (tok.type == TOKEN_FUNC)
|
if (tok.type == TOKEN_FUNC)
|
||||||
{
|
{
|
||||||
@@ -2329,29 +2329,29 @@ static inline Decl *parse_func_definition(Visibility visibility, bool is_interfa
|
|||||||
if (path || tok.type == TOKEN_TYPE_IDENT)
|
if (path || tok.type == TOKEN_TYPE_IDENT)
|
||||||
{
|
{
|
||||||
// Special case, actually an extension
|
// Special case, actually an extension
|
||||||
TRY_EXPECT_OR(TOKEN_TYPE_IDENT, "A type was expected after '::'.", false);
|
TRY_EXPECT_OR(TOKEN_TYPE_IDENT, "A type was expected after '::'.", &poisoned_decl);
|
||||||
Type *type = type_new(TYPE_USER_DEFINED);
|
Type *type = type_new(TYPE_USER_DEFINED);
|
||||||
type->unresolved.path = path;
|
type->unresolved.path = path;
|
||||||
type->name_loc = tok;
|
type->name_loc = tok;
|
||||||
func->func.type_parent = type;
|
func->func.type_parent = type;
|
||||||
advance_and_verify(TOKEN_TYPE_IDENT);
|
advance_and_verify(TOKEN_TYPE_IDENT);
|
||||||
|
|
||||||
TRY_CONSUME_OR(TOKEN_DOT, "Expected '.' after the type in a method function.", false);
|
TRY_CONSUME_OR(TOKEN_DOT, "Expected '.' after the type in a method function.", &poisoned_decl);
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPECT_IDENT_FOR_OR("function name", false);
|
EXPECT_IDENT_FOR_OR("function name", &poisoned_decl);
|
||||||
func->name = tok;
|
func->name = tok;
|
||||||
advance_and_verify(TOKEN_IDENT);
|
advance_and_verify(TOKEN_IDENT);
|
||||||
|
|
||||||
if (!parse_opt_parameter_type_list(visibility, &(func->func.function_signature), is_interface)) return false;
|
if (!parse_opt_parameter_type_list(visibility, &(func->func.function_signature), is_interface)) return &poisoned_decl;
|
||||||
|
|
||||||
if (!parse_opt_throw_declaration(&(func->func.function_signature))) return false;
|
if (!parse_opt_throw_declaration(&(func->func.function_signature))) return &poisoned_decl;
|
||||||
if (is_interface)
|
if (is_interface)
|
||||||
{
|
{
|
||||||
if (tok.type == TOKEN_LBRACE)
|
if (tok.type == TOKEN_LBRACE)
|
||||||
{
|
{
|
||||||
SEMA_ERROR(next_tok, "Functions bodies are not allowed in interface files.");
|
SEMA_ERROR(next_tok, "Functions bodies are not allowed in interface files.");
|
||||||
return false;
|
return &poisoned_decl;
|
||||||
}
|
}
|
||||||
TRY_CONSUME_OR(TOKEN_EOS, "Expected ';' after function declaration.", &poisoned_decl);
|
TRY_CONSUME_OR(TOKEN_EOS, "Expected ';' after function declaration.", &poisoned_decl);
|
||||||
return func;
|
return func;
|
||||||
|
|||||||
@@ -36,26 +36,27 @@ Decl *context_find_ident(Context *context, const char *symbol)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static inline void context_push_scope(Context *context)
|
|
||||||
|
static inline void context_push_scope_with_flags(Context *context, ScopeFlags flags)
|
||||||
{
|
{
|
||||||
if (context->current_scope == &context->scopes[MAX_SCOPE_DEPTH - 1])
|
if (context->current_scope == &context->scopes[MAX_SCOPE_DEPTH - 1])
|
||||||
{
|
{
|
||||||
FATAL_ERROR("Too deeply nested scopes.");
|
FATAL_ERROR("Too deeply nested scopes.");
|
||||||
}
|
}
|
||||||
|
ScopeFlags previous_flags = context->current_scope->flags;
|
||||||
context->current_scope++;
|
context->current_scope++;
|
||||||
context->current_scope->exit = EXIT_NONE;
|
context->current_scope->exit = EXIT_NONE;
|
||||||
context->current_scope->local_decl_start = context->last_local;
|
context->current_scope->local_decl_start = context->last_local;
|
||||||
context->current_scope->defer_start = vec_size(context->defers);
|
context->current_scope->defer_start = vec_size(context->defers);
|
||||||
}
|
|
||||||
|
|
||||||
static inline void context_push_scope_with_flags(Context *context, ScopeFlags flags)
|
|
||||||
{
|
|
||||||
ScopeFlags previous_flags = context->current_scope->flags;
|
|
||||||
context_push_scope(context);
|
|
||||||
context->current_scope->flags = previous_flags | flags;
|
context->current_scope->flags = previous_flags | flags;
|
||||||
context->current_scope->flags_created = flags;
|
context->current_scope->flags_created = flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void context_push_scope(Context *context)
|
||||||
|
{
|
||||||
|
context_push_scope_with_flags(context, SCOPE_NONE);
|
||||||
|
}
|
||||||
|
|
||||||
static inline void context_pop_scope(Context *context)
|
static inline void context_pop_scope(Context *context)
|
||||||
{
|
{
|
||||||
assert(context->current_scope != &context->scopes[0]);
|
assert(context->current_scope != &context->scopes[0]);
|
||||||
@@ -211,10 +212,17 @@ static inline bool sema_analyse_function_param(Context *context, Decl *param, bo
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool sema_analyse_function_signature(Context *context, FunctionSignature *signature, bool is_function)
|
static inline Type *sema_analyse_function_signature(Context *context, FunctionSignature *signature, bool is_function)
|
||||||
{
|
{
|
||||||
|
char buffer[2048];
|
||||||
|
size_t buffer_write_offset = 0;
|
||||||
bool all_ok = true;
|
bool all_ok = true;
|
||||||
all_ok = sema_resolve_type(context, signature->rtype) && all_ok;
|
all_ok = sema_resolve_type(context, signature->rtype) && all_ok;
|
||||||
|
if (all_ok)
|
||||||
|
{
|
||||||
|
type_append_signature_name(signature->rtype, buffer, &buffer_write_offset);
|
||||||
|
buffer[buffer_write_offset++] = '(';
|
||||||
|
}
|
||||||
// TODO check parameter name appearing more than once.
|
// TODO check parameter name appearing more than once.
|
||||||
VECEACH(signature->params, i)
|
VECEACH(signature->params, i)
|
||||||
{
|
{
|
||||||
@@ -228,12 +236,42 @@ static inline bool sema_analyse_function_signature(Context *context, FunctionSig
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
param->resolve_status = RESOLVE_DONE;
|
param->resolve_status = RESOLVE_DONE;
|
||||||
|
if (i > 0 && all_ok)
|
||||||
|
{
|
||||||
|
buffer[buffer_write_offset++] = ',';
|
||||||
}
|
}
|
||||||
|
type_append_signature_name(param->var.type, buffer, &buffer_write_offset);
|
||||||
|
}
|
||||||
|
// TODO variadic
|
||||||
|
buffer[buffer_write_offset++] = ')';
|
||||||
|
if (vec_size(signature->throws))
|
||||||
|
{
|
||||||
VECEACH(signature->throws, i)
|
VECEACH(signature->throws, i)
|
||||||
{
|
{
|
||||||
TODO
|
TODO
|
||||||
|
if (i > 0 && all_ok)
|
||||||
|
{
|
||||||
|
buffer[buffer_write_offset++] = ',';
|
||||||
}
|
}
|
||||||
return all_ok;
|
// type_append_signature_name(signature->tparam->var.type, buffer, &buffer_write_offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!all_ok) return NULL;
|
||||||
|
TokenType type = TOKEN_INVALID_TOKEN;
|
||||||
|
signature->mangled_signature = symtab_add(buffer, buffer_write_offset, fnv1a(buffer, buffer_write_offset), &type);
|
||||||
|
Type *func_type = stable_get(&context->local_symbols, signature->mangled_signature);
|
||||||
|
if (!func_type)
|
||||||
|
{
|
||||||
|
func_type = type_new(TYPE_FUNC);
|
||||||
|
func_type->name_loc = (Token) { .string = signature->mangled_signature, .span.length = buffer_write_offset };
|
||||||
|
func_type->resolve_status = RESOLVE_DONE;
|
||||||
|
func_type->canonical = func_type;
|
||||||
|
func_type->func.signature = signature;
|
||||||
|
stable_set(&context->local_symbols, signature->mangled_signature, func_type);
|
||||||
|
}
|
||||||
|
return func_type;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -602,6 +640,11 @@ static inline bool sema_analyse_goto_stmt(Context *context, Ast *statement)
|
|||||||
|
|
||||||
static inline bool sema_analyse_if_stmt(Context *context, Ast *statement)
|
static inline bool sema_analyse_if_stmt(Context *context, Ast *statement)
|
||||||
{
|
{
|
||||||
|
// IMPROVE
|
||||||
|
// convert
|
||||||
|
// if (!x) A(); else B();
|
||||||
|
// into
|
||||||
|
// if (x) B(); else A();
|
||||||
context_push_scope(context);
|
context_push_scope(context);
|
||||||
Ast *cond = statement->if_stmt.cond;
|
Ast *cond = statement->if_stmt.cond;
|
||||||
context_push_scope_with_flags(context, SCOPE_CONTROL);
|
context_push_scope_with_flags(context, SCOPE_CONTROL);
|
||||||
@@ -635,7 +678,7 @@ static inline bool sema_analyse_label(Context *context, Ast *statement)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
vec_add(context->labels, statement);
|
VECADD(context->labels, statement);
|
||||||
VECEACH(context->gotos, i)
|
VECEACH(context->gotos, i)
|
||||||
{
|
{
|
||||||
Ast *the_goto = context->gotos[i];
|
Ast *the_goto = context->gotos[i];
|
||||||
@@ -941,6 +984,7 @@ static inline bool sema_analyse_function_body(Context *context, Decl *func)
|
|||||||
}
|
}
|
||||||
sema_release_defer_chain(context, &func->func.body->compound_stmt.stmts);
|
sema_release_defer_chain(context, &func->func.body->compound_stmt.stmts);
|
||||||
}
|
}
|
||||||
|
func->func.labels = context->labels;
|
||||||
context_pop_scope(context);
|
context_pop_scope(context);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -974,15 +1018,16 @@ static inline bool sema_analyse_method_function(Context *context, Decl *decl)
|
|||||||
static inline bool sema_analyse_func(Context *context, Decl *decl)
|
static inline bool sema_analyse_func(Context *context, Decl *decl)
|
||||||
{
|
{
|
||||||
DEBUG_LOG("Analysing function %s", decl->name.string);
|
DEBUG_LOG("Analysing function %s", decl->name.string);
|
||||||
bool all_ok = sema_analyse_function_signature(context, &decl->func.function_signature, true);
|
Type *func_type = sema_analyse_function_signature(context, &decl->func.function_signature, true);
|
||||||
|
decl->self_type = func_type;
|
||||||
|
if (!func_type) return decl_poison(decl);
|
||||||
if (decl->func.type_parent)
|
if (decl->func.type_parent)
|
||||||
{
|
{
|
||||||
all_ok = all_ok && sema_analyse_method_function(context, decl);
|
if (!sema_analyse_method_function(context, decl)) return decl_poison(decl);
|
||||||
}
|
}
|
||||||
all_ok = all_ok && sema_analyse_function_body(context, decl);
|
if (!sema_analyse_function_body(context, decl)) return decl_poison(decl);
|
||||||
if (!all_ok) decl_poison(decl);
|
|
||||||
DEBUG_LOG("Function analysis done.")
|
DEBUG_LOG("Function analysis done.")
|
||||||
return all_ok;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool sema_analyse_macro(Context *context, Decl *decl)
|
static inline bool sema_analyse_macro(Context *context, Decl *decl)
|
||||||
@@ -1027,7 +1072,15 @@ static inline bool sema_analyse_global(Context *context, Decl *decl)
|
|||||||
|
|
||||||
static inline bool sema_analyse_typedef(Context *context, Decl *decl)
|
static inline bool sema_analyse_typedef(Context *context, Decl *decl)
|
||||||
{
|
{
|
||||||
|
if (decl->typedef_decl.is_func)
|
||||||
|
{
|
||||||
|
Type *func_type = sema_analyse_function_signature(context, &decl->typedef_decl.function_signature, false);
|
||||||
|
if (!func_type) return false;
|
||||||
|
decl->self_type->canonical = func_type;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (!sema_resolve_type(context, decl->typedef_decl.type)) return false;
|
if (!sema_resolve_type(context, decl->typedef_decl.type)) return false;
|
||||||
|
decl->self_type->canonical = decl->typedef_decl.type;
|
||||||
// Do we need anything else?
|
// Do we need anything else?
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -1164,7 +1217,7 @@ static inline void sema_process_imports(Context *context)
|
|||||||
void sema_analysis_pass_conditional_compilation(Context *context)
|
void sema_analysis_pass_conditional_compilation(Context *context)
|
||||||
{
|
{
|
||||||
DEBUG_LOG("Pass 1 - analyse: %s", context->file->name);
|
DEBUG_LOG("Pass 1 - analyse: %s", context->file->name);
|
||||||
VECEACH(context->ct_ifs, i)
|
for (unsigned i = 0; i < vec_size(context->ct_ifs); i++)
|
||||||
{
|
{
|
||||||
sema_analyse_top_level_if(context, context->ct_ifs[i]);
|
sema_analyse_top_level_if(context, context->ct_ifs[i]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ typedef struct
|
|||||||
File **files;
|
File **files;
|
||||||
} SourceFiles;
|
} SourceFiles;
|
||||||
|
|
||||||
SourceFiles source_files;
|
SourceFiles source_files = {};
|
||||||
|
|
||||||
File *source_file_load(const char *filename, bool *already_loaded)
|
File *source_file_load(const char *filename, bool *already_loaded)
|
||||||
{
|
{
|
||||||
@@ -45,16 +45,70 @@ File *source_file_load(const char *filename, bool *already_loaded)
|
|||||||
size_t size;
|
size_t size;
|
||||||
const char* source_text = read_file(filename, &size);
|
const char* source_text = read_file(filename, &size);
|
||||||
File *file = malloc(sizeof(File));
|
File *file = malloc(sizeof(File));
|
||||||
|
|
||||||
file->full_path = full_path;
|
file->full_path = full_path;
|
||||||
file->start_id = vec_size(source_files.files) ? VECLAST(source_files.files)->end_id : 0;
|
file->start_id = vec_size(source_files.files) ? VECLAST(source_files.files)->end_id : 0;
|
||||||
|
file->current_line_start = file->start_id;
|
||||||
file->contents = source_text;
|
file->contents = source_text;
|
||||||
ASSERT(file->start_id + size < UINT32_MAX, "Total files loaded exceeded %d bytes", UINT32_MAX);
|
ASSERT(file->start_id + size < UINT32_MAX, "Total files loaded exceeded %d bytes", UINT32_MAX);
|
||||||
file->end_id = (SourceLoc) (file->start_id + size);
|
file->end_id = (SourceLoc) (file->start_id + size);
|
||||||
file->name = filename;
|
size_t pre_allocated_lines = size / 40;
|
||||||
source_files.files = VECADD(source_files.files, file);
|
file->line_start = VECNEW(SourceLoc, pre_allocated_lines < 16 ? 16 : pre_allocated_lines);
|
||||||
|
VECADD(file->line_start, file->start_id);
|
||||||
|
path_get_dir_and_filename_from_full(file->full_path, &file->name, &file->dir_path);
|
||||||
|
VECADD(source_files.files, file);
|
||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void source_file_append_line_end(File *file, SourceLoc loc)
|
||||||
|
{
|
||||||
|
if (file->current_line_start > loc) return;
|
||||||
|
file->current_line_start = loc + 1;
|
||||||
|
VECADD(file->line_start, file->current_line_start);
|
||||||
|
}
|
||||||
|
|
||||||
|
SourcePosition source_file_find_position(SourceLoc loc)
|
||||||
|
{
|
||||||
|
File *file = source_file_from_position(loc);
|
||||||
|
return source_file_find_position_in_file(file, loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
SourcePosition source_file_find_position_in_file(File *file, SourceLoc loc)
|
||||||
|
{
|
||||||
|
assert(file->start_id <= loc);
|
||||||
|
|
||||||
|
size_t lines = vec_size(file->line_start);
|
||||||
|
unsigned low = 0;
|
||||||
|
unsigned high = lines;
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
// Line found iff line_start[mid] <= loc && line_start[mid + 1] < loc
|
||||||
|
// Binary search
|
||||||
|
uint32_t mid = (high + low) / 2;
|
||||||
|
|
||||||
|
// Mid is before the location.
|
||||||
|
SourceLoc line_start = file->line_start[mid];
|
||||||
|
if (line_start > loc)
|
||||||
|
{
|
||||||
|
high = mid;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (mid + 1 != lines && file->line_start[mid + 1] < loc)
|
||||||
|
{
|
||||||
|
low = mid;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return (SourcePosition)
|
||||||
|
{
|
||||||
|
.file = file,
|
||||||
|
.line = mid + 1,
|
||||||
|
.col = loc - line_start + 1,
|
||||||
|
.loc = loc,
|
||||||
|
.start = file->contents + loc - file->start_id,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
File *source_file_from_position(SourceLoc loc)
|
File *source_file_from_position(SourceLoc loc)
|
||||||
{
|
{
|
||||||
if (loc == INVALID_LOC)
|
if (loc == INVALID_LOC)
|
||||||
@@ -62,10 +116,12 @@ File *source_file_from_position(SourceLoc loc)
|
|||||||
pseudo_file.contents = "---";
|
pseudo_file.contents = "---";
|
||||||
return &pseudo_file;
|
return &pseudo_file;
|
||||||
}
|
}
|
||||||
if (lexer_current_file()->start_id <= loc) return lexer_current_file();
|
static File *last_file = NULL;
|
||||||
|
//if (!last_file) last_file = lexer_current_file();
|
||||||
|
//if (last_file->start_id <= loc && loc < last_file->end_id) return last_file;
|
||||||
unsigned low = 0;
|
unsigned low = 0;
|
||||||
unsigned high = vec_size(source_files.files) - 2;
|
unsigned high = vec_size(source_files.files) - 1;
|
||||||
assert(vec_size(source_files.files) > 1);
|
//assert(vec_size(source_files.files) > 1);
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
// Binary search
|
// Binary search
|
||||||
@@ -81,7 +137,7 @@ File *source_file_from_position(SourceLoc loc)
|
|||||||
low = mid + 1;
|
low = mid + 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
return file;
|
return last_file = file;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
100
src/compiler/target.c
Normal file
100
src/compiler/target.c
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
#include <llvm-c/Target.h>
|
||||||
|
#include <llvm-c/TargetMachine.h>
|
||||||
|
#include "compiler_internal.h"
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
LLVMTargetRef target;
|
||||||
|
LLVMTargetMachineRef machine;
|
||||||
|
LLVMTargetDataRef data_layout;
|
||||||
|
int alloca_address_space;
|
||||||
|
} Target;
|
||||||
|
|
||||||
|
static Target target = {};
|
||||||
|
|
||||||
|
int target_alloca_addr_space()
|
||||||
|
{
|
||||||
|
return target.alloca_address_space;
|
||||||
|
}
|
||||||
|
|
||||||
|
void target_setup()
|
||||||
|
{
|
||||||
|
assert(!target.target);
|
||||||
|
|
||||||
|
LLVMInitializeAllTargetInfos();
|
||||||
|
LLVMInitializeAllTargetMCs();
|
||||||
|
LLVMInitializeAllTargets();
|
||||||
|
LLVMInitializeAllAsmPrinters();
|
||||||
|
LLVMInitializeAllAsmParsers();
|
||||||
|
|
||||||
|
target.target = NULL;
|
||||||
|
if (!build_options.target)
|
||||||
|
{
|
||||||
|
build_options.target = LLVMGetDefaultTargetTriple();
|
||||||
|
}
|
||||||
|
char *err = NULL;
|
||||||
|
|
||||||
|
if (LLVMGetTargetFromTriple(build_options.target, &target.target, &err) != 0)
|
||||||
|
{
|
||||||
|
error_exit("Could not create target: %s", err);
|
||||||
|
// Usually we would dispose of err, but no need to do it due to exit.
|
||||||
|
}
|
||||||
|
|
||||||
|
target.alloca_address_space = 0;
|
||||||
|
|
||||||
|
DEBUG_LOG("Target set to %s.", build_options.target);
|
||||||
|
// Create a specific target machine
|
||||||
|
LLVMCodeGenOptLevel level;
|
||||||
|
LLVMRelocMode reloc_mode = LLVMRelocDefault;
|
||||||
|
|
||||||
|
switch (build_options.optimization_level)
|
||||||
|
{
|
||||||
|
case OPTIMIZATION_NOT_SET:
|
||||||
|
UNREACHABLE;
|
||||||
|
case OPTIMIZATION_AGGRESSIVE:
|
||||||
|
level = LLVMCodeGenLevelAggressive;
|
||||||
|
break;
|
||||||
|
case OPTIMIZATION_DEFAULT:
|
||||||
|
level = LLVMCodeGenLevelDefault;
|
||||||
|
break;
|
||||||
|
case OPTIMIZATION_LESS:
|
||||||
|
level = LLVMCodeGenLevelLess;
|
||||||
|
break;
|
||||||
|
case OPTIMIZATION_NONE:
|
||||||
|
level = LLVMCodeGenLevelNone;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
UNREACHABLE;
|
||||||
|
}
|
||||||
|
// reloc = (opt->pic || opt->library)? LLVMRelocPIC : LLVMRelocDefault;
|
||||||
|
if (!build_options.cpu)
|
||||||
|
{
|
||||||
|
build_options.cpu = "generic";
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
if (!opt->features)
|
||||||
|
{
|
||||||
|
opt->features = "";
|
||||||
|
}*/
|
||||||
|
if (!(target.machine = LLVMCreateTargetMachine(target.target, build_options.target, build_options.cpu, "", level, reloc_mode,
|
||||||
|
LLVMCodeModelDefault))) {
|
||||||
|
error_exit("Failed to create target machine.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// The below is broken for the AMDGPU target.
|
||||||
|
target.alloca_address_space = 0;
|
||||||
|
target.data_layout = LLVMCreateTargetDataLayout(target.machine);
|
||||||
|
build_options.pointer_size = (int)LLVMPointerSize(target.data_layout);
|
||||||
|
DEBUG_LOG("Deduced pointer size to be %d bits", build_options.pointer_size * 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
void target_destroy()
|
||||||
|
{
|
||||||
|
assert(target.machine);
|
||||||
|
LLVMDisposeTargetMachine(target.machine);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *target_data_layout()
|
||||||
|
{
|
||||||
|
return target.data_layout;
|
||||||
|
}
|
||||||
@@ -68,6 +68,7 @@ const char *type_to_error_string(Type *type)
|
|||||||
case TYPE_F32:
|
case TYPE_F32:
|
||||||
case TYPE_F64:
|
case TYPE_F64:
|
||||||
case TYPE_FXX:
|
case TYPE_FXX:
|
||||||
|
case TYPE_FUNC:
|
||||||
return type->name_loc.string;
|
return type->name_loc.string;
|
||||||
case TYPE_POINTER:
|
case TYPE_POINTER:
|
||||||
asprintf(&buffer, "%s*", type_to_error_string(type->base));
|
asprintf(&buffer, "%s*", type_to_error_string(type->base));
|
||||||
@@ -95,6 +96,101 @@ const char *type_to_error_string(Type *type)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void type_append_signature_name_user_defined(Decl *decl, char *dst, size_t *offset)
|
||||||
|
{
|
||||||
|
switch (decl->decl_kind)
|
||||||
|
{
|
||||||
|
case DECL_FUNC:
|
||||||
|
{
|
||||||
|
assert(decl->func.function_signature.mangled_signature);
|
||||||
|
int len = sprintf(dst + *offset, "func %s", decl->func.function_signature.mangled_signature);
|
||||||
|
*offset += len;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case DECL_POISONED:
|
||||||
|
case DECL_VAR:
|
||||||
|
case DECL_ENUM_CONSTANT:
|
||||||
|
case DECL_TYPEDEF:
|
||||||
|
case DECL_ERROR_CONSTANT:
|
||||||
|
case DECL_ARRAY_VALUE:
|
||||||
|
case DECL_IMPORT:
|
||||||
|
case DECL_MACRO:
|
||||||
|
case DECL_MULTI_DECL:
|
||||||
|
case DECL_GENERIC:
|
||||||
|
case DECL_CT_IF:
|
||||||
|
case DECL_CT_ELSE:
|
||||||
|
case DECL_CT_ELIF:
|
||||||
|
case DECL_ATTRIBUTE:
|
||||||
|
UNREACHABLE
|
||||||
|
case DECL_STRUCT:
|
||||||
|
case DECL_UNION:
|
||||||
|
case DECL_ENUM:
|
||||||
|
case DECL_ERROR:
|
||||||
|
memcpy(dst + *offset, decl->name.string, decl->name.span.length);
|
||||||
|
*offset += decl->name.span.length;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
UNREACHABLE
|
||||||
|
}
|
||||||
|
void type_append_signature_name(Type *type, char *dst, size_t *offset)
|
||||||
|
{
|
||||||
|
assert(*offset < 2000);
|
||||||
|
assert(type->resolve_status == RESOLVE_DONE && type->canonical && type_ok(type));
|
||||||
|
Type *canonical_type = type->canonical;
|
||||||
|
switch (type->type_kind)
|
||||||
|
{
|
||||||
|
case TYPE_POISONED:
|
||||||
|
UNREACHABLE
|
||||||
|
case TYPE_USER_DEFINED:
|
||||||
|
type_append_signature_name_user_defined(canonical_type->decl, dst, offset);
|
||||||
|
return;
|
||||||
|
case TYPE_VOID:
|
||||||
|
case TYPE_BOOL:
|
||||||
|
case TYPE_I8:
|
||||||
|
case TYPE_I16:
|
||||||
|
case TYPE_I32:
|
||||||
|
case TYPE_I64:
|
||||||
|
case TYPE_IXX:
|
||||||
|
case TYPE_U8:
|
||||||
|
case TYPE_U16:
|
||||||
|
case TYPE_U32:
|
||||||
|
case TYPE_U64:
|
||||||
|
case TYPE_UXX:
|
||||||
|
case TYPE_F32:
|
||||||
|
case TYPE_F64:
|
||||||
|
case TYPE_FXX:
|
||||||
|
case TYPE_FUNC:
|
||||||
|
memcpy(dst + *offset, type->name_loc.string, type->name_loc.span.length);
|
||||||
|
*offset += type->name_loc.span.length;
|
||||||
|
return;
|
||||||
|
case TYPE_POINTER:
|
||||||
|
type_append_signature_name(type->base, dst, offset);
|
||||||
|
return;
|
||||||
|
case TYPE_STRING:
|
||||||
|
TODO
|
||||||
|
return;
|
||||||
|
case TYPE_ARRAY:
|
||||||
|
type_append_signature_name(type->base, dst, offset);
|
||||||
|
{
|
||||||
|
int len = sprintf(dst + *offset, "[%zu]", type->len);
|
||||||
|
*offset += len;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case TYPE_VARARRAY:
|
||||||
|
type_append_signature_name(type->base, dst, offset);
|
||||||
|
dst[*offset++] = '[';
|
||||||
|
dst[*offset] = ']';
|
||||||
|
*offset += 1;
|
||||||
|
return;
|
||||||
|
case TYPE_INC_ARRAY:
|
||||||
|
TODO
|
||||||
|
return;
|
||||||
|
case TYPE_EXPRESSION:
|
||||||
|
UNREACHABLE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Type *type_new(TypeKind type_kind)
|
Type *type_new(TypeKind type_kind)
|
||||||
{
|
{
|
||||||
Type *type = malloc_arena(sizeof(Type));
|
Type *type = malloc_arena(sizeof(Type));
|
||||||
@@ -103,6 +199,7 @@ Type *type_new(TypeKind type_kind)
|
|||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
size_t type_size(Type *canonical)
|
size_t type_size(Type *canonical)
|
||||||
{
|
{
|
||||||
assert(canonical && canonical->canonical == canonical);
|
assert(canonical && canonical->canonical == canonical);
|
||||||
@@ -132,6 +229,7 @@ size_t type_size(Type *canonical)
|
|||||||
case TYPE_UXX:
|
case TYPE_UXX:
|
||||||
case TYPE_FXX:
|
case TYPE_FXX:
|
||||||
return 8;
|
return 8;
|
||||||
|
case TYPE_FUNC:
|
||||||
case TYPE_POINTER:
|
case TYPE_POINTER:
|
||||||
case TYPE_VARARRAY:
|
case TYPE_VARARRAY:
|
||||||
case TYPE_STRING:
|
case TYPE_STRING:
|
||||||
@@ -215,7 +313,7 @@ Type *type_get_canonical_array(Type *arr_type)
|
|||||||
return canonical;
|
return canonical;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void create_type(const char *name, Type *location, Type **ptr, TypeKind kind, unsigned bytesize, unsigned bitsize)
|
static void type_create(const char *name, Type *location, Type **ptr, TypeKind kind, unsigned bytesize, unsigned bitsize)
|
||||||
{
|
{
|
||||||
*location = (Type) { .type_kind = kind, .resolve_status = RESOLVE_DONE, .builtin.bytesize = bytesize, .builtin.bitsize = bitsize };
|
*location = (Type) { .type_kind = kind, .resolve_status = RESOLVE_DONE, .builtin.bytesize = bytesize, .builtin.bitsize = bitsize };
|
||||||
location->name_loc.string = name;
|
location->name_loc.string = name;
|
||||||
@@ -236,11 +334,11 @@ static void type_create_alias(const char *name, Type *location, Type **ptr, Type
|
|||||||
|
|
||||||
void builtin_setup()
|
void builtin_setup()
|
||||||
{
|
{
|
||||||
create_type("void", &t_u0, &type_void, TYPE_VOID, 1, 8);
|
type_create("void", &t_u0, &type_void, TYPE_VOID, 1, 8);
|
||||||
create_type("string", &t_str, &type_string, TYPE_STRING, build_options.pointer_size, build_options.pointer_size * 8);
|
type_create("string", &t_str, &type_string, TYPE_STRING, build_options.pointer_size, build_options.pointer_size * 8);
|
||||||
create_ptr_live_canonical(type_void);
|
create_ptr_live_canonical(type_void);
|
||||||
type_void->ptr_like_canonical[0] = &t_voidstar;
|
type_void->ptr_like_canonical[0] = &t_voidstar;
|
||||||
create_type("void*", &t_voidstar, &type_voidptr, TYPE_POINTER, 0, 0);
|
type_create("void*", &t_voidstar, &type_voidptr, TYPE_POINTER, 0, 0);
|
||||||
t_voidstar.base = type_void;
|
t_voidstar.base = type_void;
|
||||||
|
|
||||||
/*TODO
|
/*TODO
|
||||||
@@ -249,7 +347,7 @@ void builtin_setup()
|
|||||||
type_string.type_kind = TYPE_STRING;
|
type_string.type_kind = TYPE_STRING;
|
||||||
*/
|
*/
|
||||||
#define DEF_TYPE(_name, _shortname, _type, _bits) \
|
#define DEF_TYPE(_name, _shortname, _type, _bits) \
|
||||||
create_type(#_name, &_shortname, &type_ ## _name, _type, (_bits + 7) / 8, _bits);
|
type_create(#_name, &_shortname, &type_ ## _name, _type, (_bits + 7) / 8, _bits);
|
||||||
|
|
||||||
DEF_TYPE(compint, t_ixx, TYPE_IXX, 64);
|
DEF_TYPE(compint, t_ixx, TYPE_IXX, 64);
|
||||||
DEF_TYPE(compuint, t_uxx, TYPE_UXX, 64);
|
DEF_TYPE(compuint, t_uxx, TYPE_UXX, 64);
|
||||||
|
|||||||
@@ -98,8 +98,36 @@ void test_compiler(void)
|
|||||||
compiler_init();
|
compiler_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void test_file(void)
|
||||||
|
{
|
||||||
|
File file;
|
||||||
|
memset(&file, 0, sizeof(file));
|
||||||
|
file.start_id = 3;
|
||||||
|
VECADD(file.line_start, file.start_id);
|
||||||
|
TEST_ASSERT(source_file_find_position_in_file(&file, 3).line == 1, "Expected first line");
|
||||||
|
TEST_ASSERT(source_file_find_position_in_file(&file, 10).line == 1, "Expected first line");
|
||||||
|
source_file_append_line_end(&file, 9);
|
||||||
|
TEST_ASSERT(source_file_find_position_in_file(&file, 3).line == 1, "Expected first line");
|
||||||
|
TEST_ASSERT(source_file_find_position_in_file(&file, 5).line == 1, "Expected first line");
|
||||||
|
TEST_ASSERT(source_file_find_position_in_file(&file, 10).line == 2, "Expected second line");
|
||||||
|
source_file_append_line_end(&file, 19);
|
||||||
|
TEST_ASSERT(source_file_find_position_in_file(&file, 3).line == 1, "Expected first line");
|
||||||
|
TEST_ASSERT(source_file_find_position_in_file(&file, 5).line == 1, "Expected first line");
|
||||||
|
TEST_ASSERT(source_file_find_position_in_file(&file, 10).line == 2, "Expected second line");
|
||||||
|
TEST_ASSERT(source_file_find_position_in_file(&file, 15).line == 2, "Expected second line");
|
||||||
|
TEST_ASSERT(source_file_find_position_in_file(&file, 21).line == 3, "Expected third line");
|
||||||
|
source_file_append_line_end(&file, 29);
|
||||||
|
TEST_ASSERT(source_file_find_position_in_file(&file, 3).line == 1, "Expected first line");
|
||||||
|
TEST_ASSERT(source_file_find_position_in_file(&file, 5).line == 1, "Expected first line");
|
||||||
|
TEST_ASSERT(source_file_find_position_in_file(&file, 10).line == 2, "Expected second line");
|
||||||
|
TEST_ASSERT(source_file_find_position_in_file(&file, 15).line == 2, "Expected second line");
|
||||||
|
TEST_ASSERT(source_file_find_position_in_file(&file, 21).line == 3, "Expected third line");
|
||||||
|
TEST_ASSERT(source_file_find_position_in_file(&file, 25).line == 3, "Expected third line");
|
||||||
|
TEST_ASSERT(source_file_find_position_in_file(&file, 31).line == 4, "Expected fourth line");
|
||||||
|
}
|
||||||
void compiler_tests(void)
|
void compiler_tests(void)
|
||||||
{
|
{
|
||||||
|
test_file();
|
||||||
test_lexer();
|
test_lexer();
|
||||||
test_compiler();
|
test_compiler();
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include "lib.h"
|
#include "lib.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <libgen.h>
|
||||||
|
|
||||||
const char* expand_path(const char* path)
|
const char* expand_path(const char* path)
|
||||||
{
|
{
|
||||||
@@ -83,3 +84,22 @@ char *read_file(const char *path, size_t *return_size)
|
|||||||
fclose(file);
|
fclose(file);
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void path_get_dir_and_filename_from_full(const char *full_path, char **filename, char **dir_path)
|
||||||
|
{
|
||||||
|
char path[256];
|
||||||
|
size_t path_len = strlen(full_path);
|
||||||
|
if (path_len > 255) error_exit("Path %s too long.", full_path);
|
||||||
|
|
||||||
|
strncpy(path, full_path, path_len + 1);
|
||||||
|
const char *base_name = basename(path);
|
||||||
|
size_t filename_len = strlen(base_name);
|
||||||
|
*filename = malloc(filename_len + 1);
|
||||||
|
strncpy(*filename, base_name, filename_len + 1);
|
||||||
|
|
||||||
|
strncpy(path, full_path, path_len + 1);
|
||||||
|
const char *dir = dirname(path);
|
||||||
|
size_t dir_len = strlen(dir);
|
||||||
|
*dir_path = malloc(dir_len + 1);
|
||||||
|
strncpy(*dir_path, dir, dir_len + 1);
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,12 +4,12 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
#include <stdint.h>
|
#include "common.h"
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
const char* expand_path(const char* path);
|
const char* expand_path(const char* path);
|
||||||
char *read_file(const char *path, size_t *return_size);
|
char *read_file(const char *path, size_t *return_size);
|
||||||
int filename_to_module(const char *path, char buffer[MAX_IDENTIFIER_LENGTH + 1]);
|
int filename_to_module(const char *path, char buffer[MAX_IDENTIFIER_LENGTH + 1]);
|
||||||
|
void path_get_dir_and_filename_from_full(const char *full_path, char **filename, char **dir_path);
|
||||||
void init_arena(void);
|
void init_arena(void);
|
||||||
void *malloc_arena(unsigned long mem);
|
void *malloc_arena(unsigned long mem);
|
||||||
void free_arena(void);
|
void free_arena(void);
|
||||||
@@ -279,3 +279,6 @@ static inline bool is_all_lower(const char* string)
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char *strformat(const char *var, ...) __printflike(1, 2);
|
||||||
|
|
||||||
|
|||||||
24
src/utils/stringutils.c
Normal file
24
src/utils/stringutils.c
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include "lib.h"
|
||||||
|
#include "stdio.h"
|
||||||
|
|
||||||
|
char *strformat(const char *var, ...)
|
||||||
|
{
|
||||||
|
va_list list;
|
||||||
|
va_start(list, var);
|
||||||
|
int len = vsnprintf(NULL, 0, var, list);
|
||||||
|
va_end(list);
|
||||||
|
if (len == 0) return "";
|
||||||
|
va_start(list, var);
|
||||||
|
char *buffer = malloc_arena(len + 1);
|
||||||
|
int new_len = vsnprintf(buffer, len + 1, var, list);
|
||||||
|
va_end(list);
|
||||||
|
assert(len == new_len);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user