Skip to content

Commit

Permalink
Fix the weird sizeof bug (#226)
Browse files Browse the repository at this point in the history
  • Loading branch information
Akuli authored Feb 19, 2023
1 parent f84b191 commit 39af4f4
Show file tree
Hide file tree
Showing 10 changed files with 114 additions and 44 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions jou.cbp
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@
<Unit filename="src/simplify_cfg.c">
<Option compilerVar="CC" />
</Unit>
<Unit filename="src/target.c">
<Option compilerVar="CC" />
</Unit>
<Unit filename="src/tokenize.c">
<Option compilerVar="CC" />
</Unit>
Expand Down
2 changes: 1 addition & 1 deletion runtests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions src/codegen.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
20 changes: 20 additions & 0 deletions src/jou_compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <stdbool.h>
#include <stdnoreturn.h>
#include <llvm-c/Core.h>
#include <llvm-c/TargetMachine.h>
#include "util.h"

void update_jou_compiler(void);
Expand Down Expand Up @@ -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.
Expand Down
5 changes: 5 additions & 0 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
45 changes: 3 additions & 42 deletions src/run.c
Original file line number Diff line number Diff line change
Expand Up @@ -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, ...)
Expand Down
57 changes: 57 additions & 0 deletions src/target.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#include <stdlib.h>
#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 &target;
}
2 changes: 2 additions & 0 deletions stdlib/mem.jou
Original file line number Diff line number Diff line change
Expand Up @@ -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*
18 changes: 18 additions & 0 deletions tests/should_succeed/sizeof.jou
Original file line number Diff line number Diff line change
@@ -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
Expand Down

0 comments on commit 39af4f4

Please sign in to comment.