Add the ability to build static libraries. Bump version to 0.3.18

This commit is contained in:
Christoffer Lerno
2022-08-16 09:11:02 +02:00
parent 9a69a13b04
commit 9a3e7fd34b
13 changed files with 275 additions and 34 deletions

View File

@@ -35,6 +35,11 @@ jobs:
cd resources/testproject
..\..\build\${{ matrix.build_type }}\c3c.exe --debug-log run hello_world_win32
- name: Build testproject lib
run: |
cd resources/testproject
..\..\build\${{ matrix.build_type }}\c3c.exe --debug-log build hello_world_win32_lib
- name: run compiler tests
run: |
cd test
@@ -79,6 +84,11 @@ jobs:
cd resources/testproject
../../build/c3c run --debug-log
- name: Build testproject lib
run: |
cd resources/testproject
../../build/c3c build hello_world_lib --debug-log
- name: run compiler tests
run: |
cd test
@@ -110,11 +120,17 @@ jobs:
run: |
cmake -B build -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
cmake --build build
- name: Build testproject
run: |
cd resources/testproject
../../build/c3c run --debug-log
- name: Build testproject lib
run: |
cd resources/testproject
../../build/c3c build hello_world_lib --debug-log
- name: run compiler tests
run: |
cd test
@@ -232,6 +248,11 @@ jobs:
cd resources/testproject
../../build/c3c run --debug-log --forcelinker
- name: Build testproject lib
run: |
cd resources/testproject
../../build/c3c build hello_world_lib --debug-log
- name: run compiler tests
run: |
cd test

View File

@@ -25,7 +25,18 @@
},
"hello_world_win32": {
"type": "executable",
"csources": [
"./csource/**"
]
},
"hello_world_lib": {
"type": "static-lib",
"csources": [
"./csource/**"
]
},
"hello_world_win32_lib": {
"type": "static-lib",
}
},
}

View File

@@ -70,6 +70,8 @@ static void usage(void)
OUTPUT(" bench [<target>] Benchmark a target.");
OUTPUT(" clean-run [<target>] Clean, then run the target.");
OUTPUT(" compile-run <file1> [<file2> ...] Compile files then immediately run the result.");
OUTPUT(" static-lib <file1> [<file2> ...] Compile files without a project into a static library.");
OUTPUT(" dynamic-lib <file1> [<file2> ...] Compile files without a project into a dynamic library.");
OUTPUT(" headers <file1> [<file2> ...] Analyse files and generate C headers for public methods.");
OUTPUT("");
OUTPUT("Options:");
@@ -111,7 +113,7 @@ static void usage(void)
OUTPUT(" -l <library> - Link with the library provided.");
OUTPUT(" -L <library dir> - Append the directory to the linker search paths.");
OUTPUT(" -z <argument> - Send the <argument> as a parameter to the linker.");
OUTPUT(" --forcelinker - Force linker usage over using when doing non-cross linking.");
OUTPUT(" --forcelinker - Force built in linker usage when doing non-cross linking.");
OUTPUT("");
OUTPUT(" --reloc=<option> - Relocation model: none, pic, PIC, pie, PIE");
OUTPUT(" --x86vec=<option> - Set max level of vector instructions: none, mmx, sse, avx, avx512.");
@@ -241,6 +243,16 @@ static void parse_command(BuildOptions *options)
options->command = COMMAND_GENERATE_HEADERS;
return;
}
if (arg_match("static-lib"))
{
options->command = COMMAND_STATIC_LIB;
return;
}
if (arg_match("dynamic-lib"))
{
options->command = COMMAND_DYNAMIC_LIB;
return;
}
if (arg_match("build"))
{
options->command = COMMAND_BUILD;
@@ -742,10 +754,7 @@ BuildOptions parse_arguments(int argc, const char *argv[])
parse_command(&build_options);
continue;
}
if (build_options.command == COMMAND_COMPILE_RUN
|| build_options.command == COMMAND_COMPILE
|| build_options.command == COMMAND_COMPILE_ONLY
|| build_options.command == COMMAND_GENERATE_HEADERS)
if (command_is_projectless(build_options.command) || build_options.command == COMMAND_GENERATE_HEADERS)
{
append_file(&build_options);
continue;

View File

@@ -27,6 +27,8 @@ typedef enum
COMMAND_INIT,
COMMAND_BUILD,
COMMAND_COMPILE_RUN,
COMMAND_STATIC_LIB,
COMMAND_DYNAMIC_LIB,
COMMAND_RUN,
COMMAND_CLEAN_RUN,
COMMAND_CLEAN,
@@ -279,7 +281,8 @@ typedef enum
TARGET_TYPE_EXECUTABLE,
TARGET_TYPE_STATIC_LIB,
TARGET_TYPE_DYNAMIC_LIB,
TARGET_TYPE_TEST
TARGET_TYPE_OBJECT_FILES,
TARGET_TYPE_TEST,
} TargetType;
typedef struct
@@ -329,7 +332,6 @@ typedef struct
bool emit_asm : 1;
bool no_stdlib : 1;
bool emit_object_files : 1;
bool no_link : 1;
bool force_linker : 1;
OptimizationLevel optimization_level;
SizeOptimizationLevel size_optimization_level;
@@ -366,3 +368,4 @@ typedef struct
BuildOptions parse_arguments(int argc, const char *argv[]);
ArchOsTarget arch_os_target_from_string(const char *target);
bool command_is_projectless(CompilerCommand command);

View File

@@ -61,13 +61,15 @@ ArchOsTarget default_target = ELF_RISCV64;
ArchOsTarget default_target = ARCH_OS_TARGET_DEFAULT;
#endif
static bool command_is_compile(CompilerCommand command)
bool command_is_projectless(CompilerCommand command)
{
switch (command)
{
case COMMAND_COMPILE:
case COMMAND_COMPILE_ONLY:
case COMMAND_COMPILE_RUN:
case COMMAND_DYNAMIC_LIB:
case COMMAND_STATIC_LIB:
return true;
case COMMAND_MISSING:
case COMMAND_GENERATE_HEADERS:
@@ -95,9 +97,15 @@ static void update_build_target_from_options(BuildTarget *target, BuildOptions *
target->run_after_compile = true;
break;
case COMMAND_COMPILE_ONLY:
target->no_link = true;
target->type = TARGET_TYPE_OBJECT_FILES;
target->emit_object_files = true;
break;
case COMMAND_DYNAMIC_LIB:
target->type = TARGET_TYPE_DYNAMIC_LIB;
break;
case COMMAND_STATIC_LIB:
target->type = TARGET_TYPE_STATIC_LIB;
break;
default:
target->run_after_compile = false;
break;
@@ -196,7 +204,7 @@ static void update_build_target_from_options(BuildTarget *target, BuildOptions *
{
target->feature.x86_vector_capability = options->x86_vector_capability;
}
if (command_is_compile(options->command))
if (command_is_projectless(options->command))
{
target->build_dir = options->build_dir ? options->build_dir : NULL;
target->object_file_dir = options->obj_out ? options->obj_out : target->build_dir;

View File

@@ -180,13 +180,14 @@ static void load_into_build_target(JSONObject *json, const char *type, BuildTarg
target->no_stdlib = get_valid_bool(json, "nostdlib", type, false);
}
static void project_add_target(Project *project, BuildTarget *default_target, JSONObject *json, const char *name, const char *type)
static void project_add_target(Project *project, BuildTarget *default_target, JSONObject *json, const char *name, const char *type, TargetType target_type)
{
assert(json->type == J_OBJECT);
BuildTarget *target = CALLOCS(BuildTarget);
*target = *default_target;
vec_add(project->targets, target);
target->name = name;
target->type = target_type;
VECEACH(project->targets, i)
{
BuildTarget *other_target = project->targets[i];
@@ -203,15 +204,17 @@ static void project_add_target(Project *project, BuildTarget *default_target, J
static void project_add_targets(Project *project, JSONObject *project_data)
{
assert(project_data->type == J_OBJECT);
static const char* targets[4] = { [TARGET_TYPE_EXECUTABLE] = "executable",
static const char* targets[5] = { [TARGET_TYPE_EXECUTABLE] = "executable",
[TARGET_TYPE_STATIC_LIB] = "static-lib",
[TARGET_TYPE_DYNAMIC_LIB] = "dynamic-lib",
[TARGET_TYPE_TEST] = "test" };
static const char *target_desc[4] = {
[TARGET_TYPE_TEST] = "test",
[TARGET_TYPE_OBJECT_FILES] = "object-files"};
static const char *target_desc[5] = {
[TARGET_TYPE_EXECUTABLE] = "Executable",
[TARGET_TYPE_STATIC_LIB] = "Static library",
[TARGET_TYPE_DYNAMIC_LIB] = "Dynamic library",
[TARGET_TYPE_TEST] = "test suite" };
[TARGET_TYPE_TEST] = "test suite",
[TARGET_TYPE_OBJECT_FILES] = "object files"};
BuildTarget default_target = {
.optimization_level = OPTIMIZATION_DEFAULT,
@@ -248,9 +251,9 @@ static void project_add_targets(Project *project, JSONObject *project_data)
{
error_exit("Invalid data in target '%s'", key);
}
int type = get_valid_string_setting(object, "type", "Target type", targets, 0, 4, "a target type like 'executable' or 'static-lib'");
int type = get_valid_string_setting(object, "type", "Target type", targets, 0, 5, "a target type like 'executable' or 'static-lib'");
if (type < 0) error_exit("Target %s did not contain 'type' key.", key);
project_add_target(project, &default_target, object, key, target_desc[type]);
project_add_target(project, &default_target, object, key, target_desc[type], type);
}
}

View File

@@ -156,6 +156,41 @@ static const char *exe_name(void)
}
}
static const char *dynamic_lib_name(void) { return NULL; }
static const char *static_lib_name(void)
{
const char *name;
if (active_target.name)
{
name = active_target.name;
}
else
{
assert(vec_size(global_context.module_list));
Path *path = global_context.module_list[0]->name;
size_t first = 0;
for (size_t i = path->len; i > 0; i--)
{
if (path->module[i - 1] == ':')
{
first = i;
break;
}
}
name = &path->module[first];
}
switch (active_target.arch_os_target)
{
case WINDOWS_X86:
case WINDOWS_X64:
case MINGW_X64:
return str_cat(name, ".lib");
default:
return str_cat(name, ".a");
}
}
static void free_arenas(void)
{
if (debug_stats)
@@ -223,9 +258,9 @@ void compiler_compile(void)
{
for (unsigned i = 0; i < module_count; i++)
{
header_gen(modules[i]);
REMINDER("Header gen is needed");
// header_gen(modules[i]);
}
return;
}
if (active_target.check_only)
@@ -291,8 +326,14 @@ void compiler_compile(void)
compiler_ir_gen_time = bench_mark();
const char *output_exe = NULL;
if (!active_target.no_link && !active_target.test_output && (active_target.type == TARGET_TYPE_EXECUTABLE || active_target.type == TARGET_TYPE_TEST))
const char *output_static = NULL;
const char *output_dynamic = NULL;
if (!active_target.test_output)
{
switch (active_target.type)
{
case TARGET_TYPE_EXECUTABLE:
case TARGET_TYPE_TEST:
if (!global_context.main)
{
puts("No main function was found, compilation only object files are generated.");
@@ -301,6 +342,18 @@ void compiler_compile(void)
{
output_exe = exe_name();
}
break;
case TARGET_TYPE_STATIC_LIB:
output_static = static_lib_name();
break;
case TARGET_TYPE_DYNAMIC_LIB:
output_dynamic = dynamic_lib_name();
break;
case TARGET_TYPE_OBJECT_FILES:
break;
default:
UNREACHABLE
}
}
free_arenas();
@@ -389,7 +442,22 @@ void compiler_compile(void)
printf("Program finished with exit code %d.", ret);
}
}
if (output_static)
{
if (!static_lib_linker(output_static, obj_files, output_file_count))
{
error_exit("Failed to produce static library '%s'.", output_static);
}
printf("Static library '%s' created.", output_static);
}
if (output_dynamic)
{
if (!dynamic_lib_linker(output_dynamic, obj_files, output_file_count))
{
error_exit("Failed to produce static library '%s'.", output_dynamic);
}
printf("Dynamic library '%s' created.", output_dynamic);
}
free(obj_files);
}

View File

@@ -2135,6 +2135,7 @@ INLINE bool type_is_unsigned(Type *type);
INLINE bool type_is_union_or_strukt(Type *type);
INLINE bool type_flat_is_vector(Type *type);
INLINE AlignSize type_min_alignment(AlignSize a, AlignSize b);
INLINE AlignSize type_max_alignment(AlignSize a, AlignSize b);
INLINE BitSize type_bit_size(Type *type);
INLINE Type *type_vector_type(Type *type);
@@ -2607,12 +2608,22 @@ INLINE AlignSize type_min_alignment(AlignSize a, AlignSize b)
return (a | b) & (1 + ~(a | b));
}
/**
* Max highest required alignment
*/
INLINE AlignSize type_max_alignment(AlignSize a, AlignSize b)
{
return a < b ? b : a;
}
INLINE BitSize type_bit_size(Type *type)
{
return type_size(type) * 8;
}
bool obj_format_linking_supported(ObjectFormatType format_type);
bool static_lib_linker(const char *output_file, const char **files, unsigned file_count);
bool dynamic_lib_linker(const char *output_file, const char **files, unsigned file_count);
bool linker(const char *output_file, const char **files, unsigned file_count);
void platform_linker(const char *output_file, const char **files, unsigned file_count);
void platform_compiler(const char **files, unsigned file_count, const char* flags);

View File

@@ -5,12 +5,23 @@
#include <glob.h>
#endif
// Copied from wrapper.cpp
typedef enum
{
AR_GNU,
AR_DARWIN,
AR_DARWIN64,
AR_BSD,
AR_GNU64,
AR_COFF,
} ArFormat;
extern bool llvm_link_elf(const char **args, int arg_count, const char **error_string);
extern bool llvm_link_macho(const char **args, int arg_count, const char **error_string);
extern bool llvm_link_coff(const char **args, int arg_count, const char **error_string);
extern bool llvm_link_wasm(const char **args, int arg_count, const char **error_string);
extern bool llvm_link_mingw(const char **args, int arg_count, const char **error_string);
extern bool llvm_ar(const char *out_name, const char **args, size_t count, int ArFormat);
typedef enum
@@ -713,6 +724,35 @@ void platform_compiler(const char **files, unsigned file_count, const char *flag
}
}
bool dynamic_lib_linker(const char *output_file, const char **files, unsigned file_count)
{
error_exit("Apologies, dynamic libs are still not supported.");
}
bool static_lib_linker(const char *output_file, const char **files, unsigned file_count)
{
ArFormat format;
switch (platform_target.os)
{
case OS_DARWIN_TYPES:
format = AR_DARWIN;
break;
case OS_TYPE_WIN32:
format = AR_COFF;
break;
case OS_TYPE_FREE_BSD:
case OS_TYPE_NETBSD:
case OS_TYPE_OPENBSD:
format = AR_BSD;
break;
case OS_TYPE_LINUX:
default:
format = AR_GNU;
break;
}
return llvm_ar(output_file, files, file_count, format);
}
bool linker(const char *output_file, const char **files, unsigned file_count)
{
return link_exe(output_file, files, file_count);

View File

@@ -356,7 +356,7 @@ LLVMValueRef llvm_emit_coerce(GenContext *c, LLVMTypeRef coerced, BEValue *value
}
// Otherwise, do it through memory.
AlignSize max_align = MAX(value->alignment, llvm_abi_alignment(c, coerced));
AlignSize max_align = type_max_alignment(value->alignment, llvm_abi_alignment(c, coerced));
LLVMValueRef temp = llvm_emit_alloca(c, coerced, max_align, "tempcoerce");
llvm_emit_memcpy(c, temp, max_align, addr, value->alignment, source_size);
@@ -3067,16 +3067,21 @@ static void llvm_emit_else(GenContext *c, BEValue *be_value, Expr *expr)
void gencontext_emit_binary(GenContext *c, BEValue *be_value, Expr *expr, BEValue *lhs_loaded, BinaryOp binary_op)
{
// foo ?? bar
if (binary_op == BINARYOP_ELSE)
{
llvm_emit_else(c, be_value, expr);
return;
}
// foo || bar and foo && bar
if (binary_op == BINARYOP_AND || binary_op == BINARYOP_OR)
{
llvm_emit_logical_and_or(c, be_value, expr, binary_op);
return;
}
// Load if needed, otherwise use the already loaded.
BEValue lhs;
if (lhs_loaded)
{
@@ -3086,13 +3091,16 @@ void gencontext_emit_binary(GenContext *c, BEValue *be_value, Expr *expr, BEValu
{
llvm_emit_expr(c, &lhs, exprptr(expr->binary_expr.left));
}
// We need the rvalue.
llvm_value_rvalue(c, &lhs);
// Evaluate rhs
BEValue rhs;
llvm_emit_expr(c, &rhs, exprptr(expr->binary_expr.right));
llvm_value_rvalue(c, &rhs);
EMIT_LOC(c, expr);
// Comparison <=>
if (binary_op >= BINARYOP_GT && binary_op <= BINARYOP_EQ)
{
llvm_emit_comp(c, be_value, &lhs, &rhs, binary_op);
@@ -3109,7 +3117,6 @@ void gencontext_emit_binary(GenContext *c, BEValue *be_value, Expr *expr, BEValu
switch (binary_op)
{
case BINARYOP_ERROR:
case BINARYOP_ELSE:
UNREACHABLE
case BINARYOP_MULT:
if (is_float)
@@ -3200,13 +3207,13 @@ void gencontext_emit_binary(GenContext *c, BEValue *be_value, Expr *expr, BEValu
case BINARYOP_BIT_XOR:
val = LLVMBuildXor(c->builder, lhs_value, rhs_value, "xor");
break;
case BINARYOP_ELSE:
case BINARYOP_EQ:
case BINARYOP_NE:
case BINARYOP_GE:
case BINARYOP_GT:
case BINARYOP_LE:
case BINARYOP_LT:
UNREACHABLE
case BINARYOP_AND:
case BINARYOP_OR:
case BINARYOP_ASSIGN:
@@ -3220,6 +3227,7 @@ void gencontext_emit_binary(GenContext *c, BEValue *be_value, Expr *expr, BEValu
case BINARYOP_BIT_XOR_ASSIGN:
case BINARYOP_SHR_ASSIGN:
case BINARYOP_SHL_ASSIGN:
// Handled elsewhere.
UNREACHABLE
}
assert(val);
@@ -4408,7 +4416,7 @@ static inline void llvm_emit_syscall(GenContext *c, BEValue *be_value, Expr *exp
scratch_buffer_append("={eax}");
assert(arguments < 8);
static char const *regs[] = { "eax", "ebx", "ecx", "edx", "esi", "edi" };
llvm_syscall_write_regs_to_scratch(regs, MIN(arguments, 6));
llvm_syscall_write_regs_to_scratch(regs, arguments < 6 ? arguments : 6);
if (arguments == 7)
{
scratch_buffer_append(",rm");

View File

@@ -61,6 +61,8 @@ int main_real(int argc, const char *argv[])
case COMMAND_COMPILE:
case COMMAND_COMPILE_ONLY:
case COMMAND_COMPILE_RUN:
case COMMAND_DYNAMIC_LIB:
case COMMAND_STATIC_LIB:
compile_target(&build_options);
break;
case COMMAND_CLEAN:

View File

@@ -1 +1 @@
#define COMPILER_VERSION "0.3.17"
#define COMPILER_VERSION "0.3.18"

View File

@@ -1,6 +1,10 @@
// For hacking the C API
#include "llvm/IR/IRBuilder.h"
#include "llvm/Object/Archive.h"
#include "llvm/Object/ArchiveWriter.h"
#include "llvm/Object/IRObjectFile.h"
#include "llvm/Object/SymbolicFile.h"
#if LLVM_VERSION_MAJOR > 13
#define LINK_SIG \
@@ -49,6 +53,16 @@ typedef enum
MINGW
} ObjFormat;
typedef enum
{
AR_GNU,
AR_DARWIN,
AR_DARWIN64,
AR_BSD,
AR_GNU64,
AR_COFF,
} ArFormat;
static bool llvm_link(ObjFormat format, const char **args, int arg_count, const char** error_string)
@@ -108,6 +122,49 @@ static bool llvm_link(ObjFormat format, const char **args, int arg_count, const
extern "C" {
bool llvm_ar(const char *out_name, const char **args, size_t count, int ArFormat)
{
llvm::object::Archive::Kind kind;
switch (ArFormat)
{
case AR_BSD:
kind = llvm::object::Archive::K_BSD;
break;
case AR_DARWIN:
kind = llvm::object::Archive::K_DARWIN;
break;
case AR_DARWIN64:
kind = llvm::object::Archive::K_DARWIN64;
break;
case AR_GNU:
kind = llvm::object::Archive::K_GNU;
break;
case AR_GNU64:
kind = llvm::object::Archive::K_GNU64;
break;
case AR_COFF:
kind = llvm::object::Archive::K_GNU;
break;
default:
assert(false);
}
bool is_win = ArFormat == AR_COFF;
std::vector<llvm::NewArchiveMember> new_members {};
for (size_t i = 0; i < count; i++)
{
auto member = llvm::NewArchiveMember::getFile(std::string(args[i]), false);
if (!member) return false;
if (is_win)
{
// Needs relative paths.
const char *rel_name = strrchr(args[i], '/');
if (rel_name) member->MemberName = rel_name + 1;
}
new_members.push_back(std::move(*member));
}
return !llvm::writeArchive(std::string(out_name), std::move(new_members), true, kind, true, false, nullptr);
}
int llvm_version_major = LLVM_VERSION_MAJOR;
#if LLVM_VERSION_MAJOR < 13
#if _MSC_VER