LLVM Codegen

This commit is contained in:
Christoffer Lerno
2019-11-14 17:23:56 +01:00
parent 74c456a935
commit 990918b609
32 changed files with 2911 additions and 203 deletions

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -6,3 +6,4 @@
void compiler_init(); void compiler_init();
void compile_file(); void compile_file();

View 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);

View File

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

View File

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

View File

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

View File

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

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

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

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

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

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

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

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

View File

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

View File

@@ -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);
} }
VECEACH(signature->throws, i) // TODO variadic
buffer[buffer_write_offset++] = ')';
if (vec_size(signature->throws))
{ {
TODO VECEACH(signature->throws, i)
{
TODO
if (i > 0 && all_ok)
{
buffer[buffer_write_offset++] = ',';
}
// type_append_signature_name(signature->tparam->var.type, buffer, &buffer_write_offset);
}
} }
return all_ok;
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]);
} }

View File

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

View File

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

View File

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

View File

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

View File

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