diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml
index 1cc4e2bc..9486d398 100644
--- a/.github/workflows/linux.yml
+++ b/.github/workflows/linux.yml
@@ -10,7 +10,8 @@ jobs:
strategy:
matrix:
llvm-version: [11, 13]
- opt-level: ['-O0', '-O3']
+ # Testing all levels because there was a bug that only happened with -O1. (#224)
+ opt-level: ['-O0', '-O1', '-O2', '-O3']
steps:
- uses: actions/checkout@v3
- run: sudo apt install -y llvm-${{ matrix.llvm-version }}-dev clang-${{ matrix.llvm-version }} make valgrind
diff --git a/jou.cbp b/jou.cbp
index 0010181f..be02a9ca 100644
--- a/jou.cbp
+++ b/jou.cbp
@@ -68,6 +68,9 @@
+
+
+
diff --git a/runtests.sh b/runtests.sh
index e208a6a5..f2527d24 100755
--- a/runtests.sh
+++ b/runtests.sh
@@ -150,7 +150,7 @@ function run_test()
fi
show_run $joufile
- if diff -u --color=always \
+ if diff --text -u --color=always \
<(generate_expected_output $joufile $correct_exit_code | tr -d '\r') \
<(ulimit -v 500000 2>/dev/null; bash -c "$command; echo Exit code: \$?" 2>&1 | post_process_output $joufile | tr -d '\r') \
&>> $diffpath
diff --git a/src/codegen.c b/src/codegen.c
index 60dee82c..0f4d1f4a 100644
--- a/src/codegen.c
+++ b/src/codegen.c
@@ -435,6 +435,9 @@ LLVMModuleRef codegen(const CfGraphFile *cfgfile, const TypeContext *typectx)
.builder = LLVMCreateBuilder(),
};
+ LLVMSetTarget(st.module, get_target()->triple);
+ LLVMSetDataLayout(st.module, get_target()->data_layout);
+
for (GlobalVariable **v = typectx->globals.ptr; v < End(typectx->globals); v++) {
LLVMTypeRef t = codegen_type((*v)->type);
LLVMValueRef globalptr = LLVMAddGlobal(st.module, t, (*v)->name);
diff --git a/src/jou_compiler.h b/src/jou_compiler.h
index a0a6d3e7..69f99104 100644
--- a/src/jou_compiler.h
+++ b/src/jou_compiler.h
@@ -4,6 +4,7 @@
#include
#include
#include
+#include
#include "util.h"
void update_jou_compiler(void);
@@ -548,6 +549,25 @@ struct CfGraphFile {
};
+/*
+LLVM makes a mess of how to define what kind of computer will run the
+compiled programs. Sometimes it wants a target triple, sometimes a
+data layout. Sometimes it wants a string, sometimes an object
+representing the thing.
+
+This struct aims to provide everything you may ever need. Hopefully it
+will make the mess slightly less miserable to you.
+*/
+struct Target {
+ char triple[100];
+ char data_layout[500];
+ LLVMTargetRef target_ref;
+ LLVMTargetMachineRef target_machine_ref;
+ LLVMTargetDataRef target_data_ref;
+};
+void init_target(void);
+const struct Target *get_target(void);
+
/*
The compiling functions, i.e. how to go from source code to LLVM IR and
eventually running the LLVM IR. Each function's result is fed into the next.
diff --git a/src/main.c b/src/main.c
index 5c2a6372..1950d5f6 100644
--- a/src/main.c
+++ b/src/main.c
@@ -383,11 +383,16 @@ static void check_for_404_imports(const struct CompileState *compst)
int main(int argc, char **argv)
{
+ init_target();
init_types();
struct CompileState compst = { .stdlib_path = find_stdlib() };
const char *filename;
parse_arguments(argc, argv, &compst.flags, &filename);
+ if (compst.flags.verbose) {
+ printf("Target triple: %s\n", get_target()->triple);
+ printf("Data layout: %s\n", get_target()->data_layout);
+ }
#ifdef _WIN32
char *startup_path = malloc(strlen(compst.stdlib_path) + 50);
diff --git a/src/run.c b/src/run.c
index 694a5327..1f201f93 100644
--- a/src/run.c
+++ b/src/run.c
@@ -15,57 +15,18 @@
static void compile_to_object_file(LLVMModuleRef module, const char *path, const CommandLineFlags *flags)
{
- LLVMInitializeX86TargetInfo();
- LLVMInitializeX86Target();
- LLVMInitializeX86TargetMC();
- LLVMInitializeX86AsmParser();
- LLVMInitializeX86AsmPrinter();
-
- char triple[100];
-#ifdef _WIN32
- // Default is x86_64-pc-windows-msvc
- strcpy(triple, "x86_64-pc-windows-gnu");
-#else
- char *tmp = LLVMGetDefaultTargetTriple();
- assert(strlen(tmp) < sizeof triple);
- strcpy(triple, tmp);
- LLVMDisposeMessage(tmp);
-#endif
-
if (flags->verbose)
- printf("Emitting object file \"%s\" for %s\n", path, triple);
-
- char *error = NULL;
- LLVMTargetRef target = NULL;
- if (LLVMGetTargetFromTriple(triple, &target, &error)) {
- assert(error);
- fprintf(stderr, "LLVMGetTargetFromTriple failed: %s\n", error);
- exit(1);
- }
- assert(!error);
- assert(target);
-
- // The CPU name is the first component of the target triple.
- // But it's not quite that simple, achthually the typical cpu name is x86-64 instead of x86_64...
- char cpu[100];
- snprintf(cpu, sizeof cpu, "%.*s", (int)strcspn(triple, "-"), triple);
- if (!strcmp(cpu, "x86_64"))
- strcpy(cpu, "x86-64");
-
- LLVMTargetMachineRef machine = LLVMCreateTargetMachine(
- target, triple, cpu, "", LLVMCodeGenLevelDefault, LLVMRelocDefault, LLVMCodeModelDefault);
- assert(machine);
+ printf("Emitting object file \"%s\"\n", path);
char *tmppath = strdup(path);
- if (LLVMTargetMachineEmitToFile(machine, module, tmppath, LLVMObjectFile, &error)) {
+ char *error = NULL;
+ if (LLVMTargetMachineEmitToFile(get_target()->target_machine_ref, module, tmppath, LLVMObjectFile, &error)) {
assert(error);
fprintf(stderr, "failed to emit object file \"%s\": %s\n", path, error);
exit(1);
}
free(tmppath);
assert(!error);
-
- LLVMDisposeTargetMachine(machine);
}
static char *malloc_sprintf(const char *fmt, ...)
diff --git a/src/target.c b/src/target.c
new file mode 100644
index 00000000..d82af995
--- /dev/null
+++ b/src/target.c
@@ -0,0 +1,57 @@
+#include
+#include "jou_compiler.h"
+
+static struct Target target = {0};
+
+static void cleanup(void)
+{
+ LLVMDisposeTargetMachine(target.target_machine_ref);
+ LLVMDisposeTargetData(target.target_data_ref);
+}
+
+void init_target(void)
+{
+ LLVMInitializeX86TargetInfo();
+ LLVMInitializeX86Target();
+ LLVMInitializeX86TargetMC();
+ LLVMInitializeX86AsmParser();
+ LLVMInitializeX86AsmPrinter();
+
+#ifdef _WIN32
+ // Default is x86_64-pc-windows-msvc
+ strcpy(target.triple, "x86_64-pc-windows-gnu");
+#else
+ char *triple = LLVMGetDefaultTargetTriple();
+ assert(strlen(triple) < sizeof target.triple);
+ strcpy(target.triple, triple);
+ LLVMDisposeMessage(triple);
+#endif
+
+ char *error = NULL;
+ if (LLVMGetTargetFromTriple(target.triple, &target.target_ref, &error)) {
+ assert(error);
+ fprintf(stderr, "LLVMGetTargetFromTriple(\"%s\") failed: %s\n", target.triple, error);
+ exit(1);
+ }
+ assert(!error);
+ assert(target.target_ref);
+
+ target.target_machine_ref = LLVMCreateTargetMachine(
+ target.target_ref, target.triple, "x86-64", "", LLVMCodeGenLevelDefault, LLVMRelocDefault, LLVMCodeModelDefault);
+ assert(target.target_machine_ref);
+
+ target.target_data_ref = LLVMCreateTargetDataLayout(target.target_machine_ref);
+ assert(target.target_data_ref);
+
+ char *tmp = LLVMCopyStringRepOfTargetData(target.target_data_ref);
+ assert(strlen(tmp) < sizeof target.data_layout);
+ strcpy(target.data_layout, tmp);
+ LLVMDisposeMessage(tmp);
+
+ atexit(cleanup);
+}
+
+const struct Target *get_target(void)
+{
+ return ⌖
+}
diff --git a/stdlib/mem.jou b/stdlib/mem.jou
index 4c313f8c..94250f58 100644
--- a/stdlib/mem.jou
+++ b/stdlib/mem.jou
@@ -4,3 +4,5 @@
# TODO: write a tutorial about using these and add a link
declare malloc(size: long) -> void*
declare free(ptr: void*) -> void
+
+declare memcpy(dest: void*, source: void*, count: long) -> void*
diff --git a/tests/should_succeed/sizeof.jou b/tests/should_succeed/sizeof.jou
index 6fee9a19..ed08359c 100644
--- a/tests/should_succeed/sizeof.jou
+++ b/tests/should_succeed/sizeof.jou
@@ -1,10 +1,28 @@
from "stdlib/io.jou" import printf
+from "stdlib/mem.jou" import memcpy, malloc, free
def side_effect() -> int:
printf("Side Effect !!!!!\n")
return 123
+struct Foo:
+ a: int
+ b: long
+ c: byte
+
+# See issue #224.
+def ensure_sizeof_isnt_too_small_in_a_weird_corner_case() -> void:
+ value = Foo{a=1, b=2, c='x'}
+ # We need the heap allocation, because otherwise the optimizer happens to make things work.
+ ptr = malloc(50) as Foo*
+ memcpy(ptr, &value, sizeof value)
+ # If sizeof is too small, this prints garbage.
+ printf("%c\n", ptr->c) # Output: x
+ free(ptr)
+
def main() -> int:
+ ensure_sizeof_isnt_too_small_in_a_weird_corner_case()
+
b: byte
n: int
m: long