From 9ab14ca6a5ddb2c228cb2e23f3cab62988958337 Mon Sep 17 00:00:00 2001 From: Akuli Date: Thu, 14 Dec 2023 17:40:42 +0200 Subject: [PATCH 01/10] class field pointer hell --- self_hosted/create_llvm_ir.jou | 41 +++++++++++++++++++++++++--------- self_hosted/llvm.jou | 2 ++ self_hosted/runs_wrong.txt | 1 - self_hosted/typecheck.jou | 2 ++ self_hosted/types.jou | 1 + src/codegen.c | 6 ++--- 6 files changed, 38 insertions(+), 15 deletions(-) diff --git a/self_hosted/create_llvm_ir.jou b/self_hosted/create_llvm_ir.jou index dd5e7b92..21ccdc2b 100644 --- a/self_hosted/create_llvm_ir.jou +++ b/self_hosted/create_llvm_ir.jou @@ -65,11 +65,16 @@ def create_llvm_union_type(types: LLVMType**, ntypes: int) -> LLVMType*: return LLVMArrayType(LLVMInt64Type(), ((size_needed + 7) / 8) as int) # ceil division + +# Pointers in classes are stored as i8*, so that a struct can contain a pointer to itself. +def field_uses_i8_ptr_hack(field: ClassField*) -> bool: + return field->type->kind == TypeKind::Pointer + + def class_type_to_llvm(fields: ClassField*, nfields: int) -> LLVMType*: elem_types: LLVMType** = malloc(nfields * sizeof elem_types[0]) for i = 0; i < nfields; i++: - # Store all pointers in structs as i8*, so that a struct can contain a pointer to itself for example. - if fields[i].type->kind == TypeKind::Pointer: + if field_uses_i8_ptr_hack(&fields[i]): elem_types[i] = LLVMPointerType(LLVMInt8Type(), 0) else: elem_types[i] = type_to_llvm(fields[i].type) @@ -224,6 +229,13 @@ class AstToIR: printf("unimplemented cast: %s --> %s\n", from->name, to->name) assert False + # Makes a temporary pointer, places the value there, then casts and reads the pointer. + def do_cast_through_pointers(self, value: LLVMValue*, to: LLVMType*) -> LLVMValue*: + p1 = LLVMBuildAlloca(self->builder, LLVMTypeOf(value), "cast_through_ptr_temp") + LLVMBuildStore(self->builder, value, p1) + p2 = LLVMBuildBitCast(self->builder, p1, LLVMPointerType(to, 0), "cast_through_ptr_temp") + return LLVMBuildLoad(self->builder, p2, "cast_through_ptr_result") + def do_binop( self, op: AstExpressionKind, @@ -390,19 +402,21 @@ class AstToIR: assert field != NULL field_pointer = LLVMBuildStructGEP2( self->builder, - type_to_llvm(class_type), instance_pointer, + type_to_llvm(class_type), + instance_pointer, field->union_id, field->name, ) - # This cast is needed for two reasons two cases: - # * All pointers are i8* in structs so we can do self-referencing classes. - # * This is how unions work. - return LLVMBuildBitCast( - self->builder, - field_pointer, LLVMPointerType(type_to_llvm(field->type),0), - "struct_member_cast", - ) + if field_uses_i8_ptr_hack(field) or field->belongs_to_union: + field_pointer = LLVMBuildBitCast( + self->builder, + field_pointer, + LLVMPointerType(type_to_llvm(field->type), 0), + "class_field_ptr_cast", + ) + + return field_pointer if ast->kind == AstExpressionKind::Indexing: # &pointer[index] = pointer + some offset @@ -568,6 +582,8 @@ class AstToIR: field = instance_type->class_members.find_field(ast->instantiation.field_names[i]) assert field != NULL value = self->do_expression(&ast->instantiation.field_values[i]) + if field_uses_i8_ptr_hack(field) or field->belongs_to_union: + value = self->do_cast_through_pointers(value, LLVMStructGetTypeAtIndex(type_to_llvm(instance_type), field->union_id)) result = LLVMBuildInsertValue(self->builder, result, value, field->union_id, "instance") elif ast->kind == AstExpressionKind::GetClassField: @@ -584,6 +600,9 @@ class AstToIR: assert field != NULL result = LLVMBuildExtractValue(self->builder, instance, field->union_id, field->name) + if field_uses_i8_ptr_hack(field) or field->belongs_to_union: + result = self->do_cast_through_pointers(result, type_to_llvm(field->type)) + elif ast->kind == AstExpressionKind::GetVariable: v = get_special_constant(ast->varname) if v == -1: diff --git a/self_hosted/llvm.jou b/self_hosted/llvm.jou index 8001c04d..c4423e61 100644 --- a/self_hosted/llvm.jou +++ b/self_hosted/llvm.jou @@ -198,6 +198,7 @@ declare LLVMDisposeModule(M: LLVMModule*) -> None declare LLVMGetSourceFileName(M: LLVMModule*, Len: long*) -> byte* # Return value not owned declare LLVMSetDataLayout(M: LLVMModule*, DataLayoutStr: byte*) -> None declare LLVMSetTarget(M: LLVMModule*, Triple: byte*) -> None +declare LLVMDumpType(Val: LLVMType*) -> None declare LLVMDumpModule(M: LLVMModule*) -> None declare LLVMPrintModuleToString(M: LLVMModule*) -> byte* declare LLVMAddFunction(M: LLVMModule*, Name: byte*, FunctionTy: LLVMType*) -> LLVMValue* @@ -212,6 +213,7 @@ declare LLVMIntType(NumBits: int) -> LLVMType* declare LLVMGetReturnType(FunctionTy: LLVMType*) -> LLVMType* declare LLVMGetParam(Fn: LLVMValue*, Index: int) -> LLVMValue* declare LLVMGetElementType(Ty: LLVMType*) -> LLVMType* +declare LLVMStructGetTypeAtIndex(StructTy: LLVMType*, i: int) -> LLVMType* declare LLVMTypeOf(Val: LLVMValue*) -> LLVMType* declare LLVMConstNull(Ty: LLVMType*) -> LLVMValue* declare LLVMGetUndef(Ty: LLVMType*) -> LLVMValue* diff --git a/self_hosted/runs_wrong.txt b/self_hosted/runs_wrong.txt index 02c37c70..d6ecf9bb 100644 --- a/self_hosted/runs_wrong.txt +++ b/self_hosted/runs_wrong.txt @@ -4,7 +4,6 @@ tests/other_errors/missing_value_in_return.jou tests/other_errors/noreturn_but_return_with_value.jou tests/other_errors/noreturn_but_return_without_value.jou tests/should_succeed/compiler_cli.jou -tests/should_succeed/linked_list.jou tests/should_succeed/pointer.jou tests/should_succeed/printf.jou tests/other_errors/return_void.jou diff --git a/self_hosted/typecheck.jou b/self_hosted/typecheck.jou index 2b9d18f5..9265f5a8 100644 --- a/self_hosted/typecheck.jou +++ b/self_hosted/typecheck.jou @@ -419,6 +419,7 @@ def handle_class_members_stage2(ft: FileTypes*, classdef: AstClassDef*) -> None: name = member->field.name, type = type_from_ast(ft, &member->field.type), union_id = union_id++, + belongs_to_union = False, } elif member->kind == AstClassMemberKind::Union: uid = union_id++ @@ -428,6 +429,7 @@ def handle_class_members_stage2(ft: FileTypes*, classdef: AstClassDef*) -> None: name = member->union_fields.fields[k].name, type = type_from_ast(ft, &member->union_fields.fields[k].type), union_id = uid, + belongs_to_union = True, } elif member->kind == AstClassMemberKind::Method: # Don't handle the method body yet: that is a part of stage 3, not stage 2 diff --git a/self_hosted/types.jou b/self_hosted/types.jou index 5656fa0e..7f7158ed 100644 --- a/self_hosted/types.jou +++ b/self_hosted/types.jou @@ -30,6 +30,7 @@ class ClassField: # If multiple fields have the same union_id, they belong to the same union. # It means that only one of the fields can be used at a time. union_id: int + belongs_to_union: bool # are there more fields with same union_id class ClassMembers: fields: ClassField* diff --git a/src/codegen.c b/src/codegen.c index 313b5de4..e2d75753 100644 --- a/src/codegen.c +++ b/src/codegen.c @@ -78,8 +78,8 @@ static LLVMTypeRef codegen_type(const Type *type) LLVMTypeRef *flat_elems = malloc(sizeof(flat_elems[0]) * n); // NOLINT for (int i = 0; i < n; i++) { - // Treat all pointers inside structs as if they were void*. - // This allows structs to contain pointers to themselves. + // Treat all pointers inside classes as if they were void*. + // This allows classes to contain pointers to themselves. if (type->data.classdata.fields.ptr[i].type->kind == TYPE_POINTER) flat_elems[i] = codegen_type(voidPtrType); else @@ -323,7 +323,7 @@ static void codegen_instruction(const struct State *st, const CfInstruction *ins LLVMValueRef val = LLVMBuildStructGEP2(st->builder, codegen_type(classtype), getop(0), f->union_id, ins->data.fieldname); // This cast is needed in two cases: - // * All pointers are i8* in structs so we can do self-referencing classes. + // * All pointers are i8* in classes so we can do self-referencing classes. // * This is how unions work. val = LLVMBuildBitCast(st->builder, val, LLVMPointerType(codegen_type(f->type),0), "struct_member_cast"); setdest(val); From a58e758be1e8d8b91fe845c78e45ab6cda633666 Mon Sep 17 00:00:00 2001 From: Akuli Date: Sun, 17 Dec 2023 01:25:47 +0200 Subject: [PATCH 02/10] pile of asserts and such --- self_hosted/create_llvm_ir.jou | 6 +++++- self_hosted/llvm.jou | 3 +++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/self_hosted/create_llvm_ir.jou b/self_hosted/create_llvm_ir.jou index 21ccdc2b..ea9349c4 100644 --- a/self_hosted/create_llvm_ir.jou +++ b/self_hosted/create_llvm_ir.jou @@ -85,9 +85,12 @@ def class_type_to_llvm(fields: ClassField*, nfields: int) -> LLVMType*: end = start + 1 while end < nfields and fields[start].union_id == fields[end].union_id: end++ + assert fields[start].union_id == combined_len elem_types[combined_len++] = create_llvm_union_type(&elem_types[start], end-start) result = LLVMStructType(elem_types, combined_len, False as int) + for i = 0; i < combined_len; i++: + assert LLVMStructGetTypeAtIndex(result, i) == elem_types[i] free(elem_types) return result @@ -234,7 +237,7 @@ class AstToIR: p1 = LLVMBuildAlloca(self->builder, LLVMTypeOf(value), "cast_through_ptr_temp") LLVMBuildStore(self->builder, value, p1) p2 = LLVMBuildBitCast(self->builder, p1, LLVMPointerType(to, 0), "cast_through_ptr_temp") - return LLVMBuildLoad(self->builder, p2, "cast_through_ptr_result") + return LLVMBuildLoad2(self->builder, to, p2, "cast_through_ptr_result") def do_binop( self, @@ -584,6 +587,7 @@ class AstToIR: value = self->do_expression(&ast->instantiation.field_values[i]) if field_uses_i8_ptr_hack(field) or field->belongs_to_union: value = self->do_cast_through_pointers(value, LLVMStructGetTypeAtIndex(type_to_llvm(instance_type), field->union_id)) + assert LLVMTypeOf(value) == LLVMStructGetTypeAtIndex(type_to_llvm(instance_type), field->union_id) result = LLVMBuildInsertValue(self->builder, result, value, field->union_id, "instance") elif ast->kind == AstExpressionKind::GetClassField: diff --git a/self_hosted/llvm.jou b/self_hosted/llvm.jou index c4423e61..abd7b1ee 100644 --- a/self_hosted/llvm.jou +++ b/self_hosted/llvm.jou @@ -215,6 +215,7 @@ declare LLVMGetParam(Fn: LLVMValue*, Index: int) -> LLVMValue* declare LLVMGetElementType(Ty: LLVMType*) -> LLVMType* declare LLVMStructGetTypeAtIndex(StructTy: LLVMType*, i: int) -> LLVMType* declare LLVMTypeOf(Val: LLVMValue*) -> LLVMType* +declare LLVMAlignOf(Ty: LLVMType*) -> LLVMValue* declare LLVMConstNull(Ty: LLVMType*) -> LLVMValue* declare LLVMGetUndef(Ty: LLVMType*) -> LLVMValue* declare LLVMConstInt(IntTy: LLVMType*, N: long, SignExtend: int) -> LLVMValue* @@ -252,8 +253,10 @@ declare LLVMBuildXor(Builder: LLVMBuilder*, LHS: LLVMValue*, RHS: LLVMValue*, Na declare LLVMBuildNeg(Builder: LLVMBuilder*, V: LLVMValue*, Name: byte*) -> LLVMValue* declare LLVMBuildFNeg(Builder: LLVMBuilder*, V: LLVMValue*, Name: byte*) -> LLVMValue* declare LLVMBuildMemSet(Builder: LLVMBuilder*, Ptr: LLVMValue*, Val: LLVMValue*, Len: LLVMValue*, Align: int) -> LLVMValue* +declare LLVMBuildMemCpy(Builder: LLVMBuilder*, Dst: LLVMValue*, DstAlign: int, Src: LLVMValue*, SrcAlign: int, Size: LLVMValue*) -> LLVMValue* declare LLVMBuildAlloca(Builder: LLVMBuilder*, Ty: LLVMType*, Name: byte*) -> LLVMValue* declare LLVMBuildLoad(Builder: LLVMBuilder*, PointerVal: LLVMValue*, Name: byte*) -> LLVMValue* +declare LLVMBuildLoad2(Builder: LLVMBuilder*, Ty: LLVMType*, PointerVal: LLVMValue*, Name: byte*) -> LLVMValue* declare LLVMBuildStore(Builder: LLVMBuilder*, Val: LLVMValue*, Ptr: LLVMValue*) -> LLVMValue* declare LLVMBuildGEP(Builder: LLVMBuilder*, Pointer: LLVMValue*, Indices: LLVMValue**, NumIndices: int, Name: byte*) -> LLVMValue* declare LLVMBuildStructGEP2(Builder: LLVMBuilder*, Ty: LLVMType*, Pointer: LLVMValue*, Idx: int, Name: byte*) -> LLVMValue* From e78c8fe57910e2c117929a0ea033c5dbc6f30597 Mon Sep 17 00:00:00 2001 From: Akuli Date: Sun, 17 Dec 2023 01:28:41 +0200 Subject: [PATCH 03/10] TEMPORARY debugs --- .github/workflows/autofix.yml | 22 ----- .github/workflows/linux.yml | 80 ---------------- .github/workflows/macos.yml | 42 -------- .github/workflows/release.yml | 89 ----------------- .github/workflows/windows.yml | 176 +--------------------------------- 5 files changed, 4 insertions(+), 405 deletions(-) delete mode 100644 .github/workflows/autofix.yml delete mode 100644 .github/workflows/linux.yml delete mode 100644 .github/workflows/macos.yml delete mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/autofix.yml b/.github/workflows/autofix.yml deleted file mode 100644 index c9b34895..00000000 --- a/.github/workflows/autofix.yml +++ /dev/null @@ -1,22 +0,0 @@ -on: - push: - branches: - - main - pull_request_target: - -jobs: - fix-line-endings: - runs-on: ubuntu-latest - steps: - # git-auto-commit-action is a bit tricky to use, see its README - - uses: actions/checkout@v3 - with: - repository: ${{ github.event.pull_request.head.repo.full_name }} - ref: ${{ github.head_ref }} - - run: sudo apt install -y dos2unix - - run: dos2unix $(git ls-files | grep -vx tests/should_succeed/crlf.jou) - - run: unix2dos tests/should_succeed/crlf.jou - - uses: stefanzweifel/git-auto-commit-action@v4 - with: - commit_message: "Change files to use LF line endings" - diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml deleted file mode 100644 index 1d08b5e9..00000000 --- a/.github/workflows/linux.yml +++ /dev/null @@ -1,80 +0,0 @@ -on: - push: - branches: - - main - pull_request: - -jobs: - shellcheck: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - run: shellcheck --color=always --shell=bash --exclude=SC2086,SC2059,SC2046,SC2235,SC2002,SC2206,SC2068,SC2207,SC2013 *.sh activate - - test: - runs-on: ubuntu-latest - strategy: - matrix: - llvm-version: [11, 13] - # 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 update - - run: sudo apt install -y llvm-${{ matrix.llvm-version }}-dev clang-${{ matrix.llvm-version }} make valgrind - - run: LLVM_CONFIG=llvm-config-${{ matrix.llvm-version }} make - - run: ./runtests.sh --verbose --jou-flags "${{ matrix.opt-level }}" - - run: ./runtests.sh --verbose --jou-flags "${{ matrix.opt-level }} --verbose" - # Valgrinding is slow, but many files affect valgrind results. - # We skip it when all changes are to .md files (docs, README etc) - - name: Figure out if we need to run tests with valgrind - id: check-need-valgrind - run: | - git fetch https://github.com/Akuli/jou main - # Find modified non-markdown files. If there are any, set doit=yes. - if git diff --name-only FETCH_HEAD HEAD | grep -vE '\.md$'; then - echo doit=yes >> $GITHUB_OUTPUT - else - echo doit=no >> $GITHUB_OUTPUT - fi - - if: ${{ steps.check-need-valgrind.outputs.doit == 'yes' }} - run: ./runtests.sh --verbose --valgrind --jou-flags "${{ matrix.opt-level }}" - # valgrind+verbose isn't meaningful: test script would ignore valgrind output - - run: make clean - - name: Check that "make clean" deleted all files not committed to Git - run: | - if [ "$(git status --porcelain --ignored)" != "" ]; then - git status --ignored - exit 1 - fi - - doctest: - runs-on: ubuntu-latest - strategy: - matrix: - llvm-version: [11, 13] - steps: - - uses: actions/checkout@v3 - - run: sudo apt update - - run: sudo apt install -y llvm-${{ matrix.llvm-version }}-dev clang-${{ matrix.llvm-version }} make - - run: LLVM_CONFIG=llvm-config-${{ matrix.llvm-version }} make - - run: ./doctest.sh - - compare-compilers: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - run: sudo apt update - - run: sudo apt install -y llvm-13-dev clang-13 make valgrind - - run: LLVM_CONFIG=llvm-config-13 make - - run: ./compare_compilers.sh - - editorconfig-checker: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - # Hard-coded commit, because the latest changes aren't tagged. - - uses: editorconfig-checker/action-editorconfig-checker@d4fca16fc71adef10fbe101903b654449fa9570c - with: - version: 2.7.0 - - run: editorconfig-checker diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml deleted file mode 100644 index 72bad46c..00000000 --- a/.github/workflows/macos.yml +++ /dev/null @@ -1,42 +0,0 @@ -on: - push: - branches: - - main - pull_request: - -jobs: - test: - runs-on: macos-latest - strategy: - matrix: - # 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: brew install bash diffutils llvm@13 - - run: make - - run: ./runtests.sh --verbose --jou-flags "${{ matrix.opt-level }}" - - run: ./runtests.sh --verbose --jou-flags "${{ matrix.opt-level }} --verbose" - - run: make clean - - name: Check that "make clean" deleted all files not committed to Git - shell: bash - run: | - if [ "$(git status --porcelain --ignored)" != "" ]; then - git status --ignored - exit 1 - fi - - doctest: - runs-on: macos-latest - steps: - - uses: actions/checkout@v3 - - run: brew install bash diffutils llvm@13 - - run: make - - run: ./doctest.sh - - compare-compilers: - runs-on: macos-latest - steps: - - uses: actions/checkout@v3 - - run: brew install bash diffutils llvm@13 - - run: ./compare_compilers.sh diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index d46dea47..00000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,89 +0,0 @@ -# -# -# -# *** If you edit this file, make sure that CONTRIBUTING.md stays up to date. -# -# -# -on: - schedule: - - cron: '0 4 * * *' - workflow_dispatch: # Can also be triggered manually from github UI - -jobs: - release: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - run: git fetch --tags - - id: tagname - run: | - if git tag -l --contains HEAD | grep .; then - # Commit already tagged - echo datetag= >> $GITHUB_OUTPUT - else - echo datetag=$(date +'%Y-%m-%d-%H00') > $GITHUB_OUTPUT - fi - - if: ${{ steps.tagname.outputs.datetag != '' }} - name: Download latest GitHub actions build results - uses: actions/github-script@v6 - with: - github-token: ${{secrets.GITHUB_TOKEN}} - script: | - const fs = require('fs'); - - // Based on: https://github.com/python/typeshed/blob/82fa8473ffddc57a53b4dbcb1063aa2e66352ca9/.github/workflows/mypy_primer_comment.yml - const allRuns = ( - await github.rest.actions.listWorkflowRunsForRepo({ - owner: context.repo.owner, - repo: context.repo.repo, - branch: 'main', - }) - ).data.workflow_runs; - console.log(`Found ${allRuns.length} runs`); - console.log(allRuns.map(r => r.name)); - const run = allRuns - .filter(r => r.name === '.github/workflows/windows.yml') - .sort((a, b) => (+new Date(b.run_started_at)) - (+new Date(a.run_started_at)))[0]; - - const allArtifacts = ( - await github.rest.actions.listWorkflowRunArtifacts({ - owner: context.repo.owner, - repo: context.repo.repo, - run_id: run.id, - }) - ).data.artifacts; - console.log(`Found ${allArtifacts.length} artifacts`); - console.log(allArtifacts.map(a => a.name)); - const artifact = allArtifacts.filter(a => a.name === 'windows-zip')[0]; - - const zip = await github.rest.actions.downloadArtifact({ - owner: context.repo.owner, - repo: context.repo.repo, - artifact_id: artifact.id, - archive_format: 'zip', - }); - - // https://stackoverflow.com/a/46779188 - fs.writeFileSync("nested-zip-file.zip", Buffer.from(zip.data)); - - # We get a zip file inside a zip file: - # * Inner zip file: The build creates a releasing-ready zip file. This is - # good because you can test the zip file before Jou is released. - # * Outer zip file: It is possible to include multiple files to the same - # GitHub Actions artifact, and downloadArtifact() gives a zip of all - # files that the artifact consists of. - - if: ${{ steps.tagname.outputs.datetag != '' }} - run: unzip nested-zip-file.zip - - - if: ${{ steps.tagname.outputs.datetag != '' }} - run: mv jou.zip jou_windows_64bit_${{ steps.tagname.outputs.datetag }}.zip - - - if: ${{ steps.tagname.outputs.datetag != '' }} - name: Create release - uses: ncipollo/release-action@v1 - with: - commit: main - tag: ${{ steps.tagname.outputs.datetag }} - artifacts: jou_windows_64bit_${{ steps.tagname.outputs.datetag }}.zip - skipIfReleaseExists: true diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 1f959b83..0252eca9 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -12,183 +12,15 @@ on: pull_request: jobs: - # I created a zip file that contains mingw64, but with some large files deleted. - # These large files are most of the time unnecessary for developing Jou. - # People with slow internet need the smaller zip file. - # - # This check fails if the zip file contains anything else than what's in the original/full zip file. - # It serves two purposes: - # * People can trust the random zip file I have created locally on my system and committed. - # * I can be sure that I didn't accidentally include something unnecessary or do something else dumb. - check-small-mingw64: - runs-on: ubuntu-latest - steps: - # Same URLs as in windows_setup.sh - - name: Download the small mingw64 - run: curl -L -o mingw64-small.zip https://akuli.github.io/mingw64-small.zip - - name: Download the full mingw64 - run: curl -L -o mingw64.zip https://github.com/brechtsanders/winlibs_mingw/releases/download/12.1.0-14.0.6-10.0.0-msvcrt-r3/winlibs-x86_64-posix-seh-gcc-12.1.0-llvm-14.0.6-mingw-w64msvcrt-10.0.0-r3.zip - # Same SHA hashes as in windows_setup.sh - - name: Veriy the small mingw64 - run: | - if [ "$(sha256sum mingw64-small.zip | cut -d' ' -f1)" != "4d858bd22f084ae362ee6a22a52c2c5b5281d996f96693984a31336873b92686" ]; then - echo "verifying failed" - exit 1 - fi - - name: Veriy the full mingw64 - run: | - if [ "$(sha256sum mingw64.zip | cut -d' ' -f1)" != "9ffef7f7a8dab893bd248085fa81a5a37ed6f775ae220ef673bea8806677836d" ]; then - echo "verifying failed" - exit 1 - fi - - name: Make sure that all file paths start with mingw64 - run: | - zipinfo -1 mingw64.zip > output.txt - zipinfo -1 mingw64-small.zip >> output.txt - cat output.txt - if [ "$(cut -d/ -f1 output.txt | uniq)" != "mingw64" ]; then - exit 1 - fi - - run: ls -lh mingw64*.zip - - name: Extract mingw64.zip - run: mkdir full && cd full && unzip ../mingw64.zip - - name: Extract mingw64-small.zip - run: mkdir small && cd small && unzip ../mingw64-small.zip - - name: Compare files - run: | - # diff exits with status 1 because the folders differ. - # Put errors to output.txt as well, so that we notice if something goes wrong. - (diff -r full small || true) &> output.txt - cat output.txt - if [ "$(cut -d/ -f1 output.txt | uniq)" != "Only in full" ]; then - exit 1 - fi - - build-zip: - runs-on: windows-latest - steps: - - uses: actions/checkout@v3 - # TODO: figure out why --small doesn't work here - - run: source activate && ./windows_setup.sh - shell: bash - - run: source activate && mingw32-make - shell: bash - # We don't need to copy all of mingw64. We only need the GNU linker. - # The less we copy, the smaller the resulting zip becomes. - # - # gcc is needed only to run the linker. It would be possible to invoke ld.exe - # directly, but the command-line it wants is a bit complicated and it's just - # easier to let a C compiler figure it out. I also tried using clang instead - # of gcc, but it depends on many LLVM DLLs and the zip file became huge. - - name: Copy the linker from mingw64 to jou/mingw64 - shell: bash - run: | - # Executables are copied without their DLLs (added later) - # Names of .a files figured out by deleting them all and looking at error message. - # CRT (C RunTime) files are needed because the linker implicitly adds them to every executable. - for file in \ - mingw64/version_info.txt \ - $(find mingw64 -name 'crt*.o') \ - mingw64/bin/gcc.exe \ - mingw64/lib/gcc/x86_64-w64-mingw32/12.1.0/libgcc.a \ - mingw64/lib/gcc/x86_64-w64-mingw32/12.1.0/libgcc_eh.a \ - mingw64/libexec/gcc/x86_64-w64-mingw32/12.1.0/liblto_plugin.dll \ - mingw64/x86_64-w64-mingw32/bin/ld.exe \ - mingw64/x86_64-w64-mingw32/lib/libadvapi32.a \ - mingw64/x86_64-w64-mingw32/lib/libkernel32.a \ - mingw64/x86_64-w64-mingw32/lib/libm.a \ - mingw64/x86_64-w64-mingw32/lib/libmingw32.a \ - mingw64/x86_64-w64-mingw32/lib/libmingwex.a \ - mingw64/x86_64-w64-mingw32/lib/libmoldname.a \ - mingw64/x86_64-w64-mingw32/lib/libmsvcrt.a \ - mingw64/x86_64-w64-mingw32/lib/libpthread.a \ - mingw64/x86_64-w64-mingw32/lib/libshell32.a \ - mingw64/x86_64-w64-mingw32/lib/libuser32.a - do - mkdir -vp jou/$(dirname $file) - cp -v $file jou/$file - done - - name: Copy more files to jou/ - # Please keep this list of files in sync with update.ps1 - run: cp -rv stdlib doc examples LICENSE jou.exe update.ps1 jou - shell: bash - - name: Copy missing DLL files to jou/ - shell: bash - run: | - function copy_dlls() { - local from=$1 - local to=$2 - local queue=($to/*.exe) - while [ ${#queue[@]} != 0 ]; do - local args=(${queue[@]}) - queue=() - for dll in $(mingw64/bin/objdump -p ${args[@]} | grep 'DLL Name:' | cut -d: -f2 | sort -u); do - if [ -f $from/$dll ] && ! [ -f $to/$dll ]; then - cp -v $from/$dll $to/ - queue+=($to/$dll) - fi - done - done - } - copy_dlls mingw64/bin jou - copy_dlls mingw64/bin jou/mingw64/bin - copy_dlls mingw64/x86_64-w64-mingw32/bin jou/mingw64/x86_64-w64-mingw32/bin - - name: Convert text files to Windows-style CRLF line endings - run: mingw64/bin/unix2dos $(find jou -name '*.jou') $(find jou -name '*.md') jou/LICENSE - shell: bash - - run: Compress-Archive -Path jou -DestinationPath jou.zip - - name: Display size of jou.zip - run: ls -lh jou.zip - shell: bash - - uses: actions/upload-artifact@v3 - with: - name: windows-zip - path: jou.zip - - test: - runs-on: windows-latest - steps: - - uses: actions/checkout@v3 - with: - # Add a space in the folder name to trigger bugs like #165 - path: "test dir" - - run: cd "test dir" && ./windows_setup.sh --small - shell: bash - - run: cd "test dir" && source activate && ./runtests.sh --verbose - shell: bash - - doctest: - runs-on: windows-latest - steps: - - uses: actions/checkout@v3 - - run: ./windows_setup.sh --small - shell: bash - - run: source activate && ./doctest.sh - shell: bash - - test-zip: - needs: build-zip - runs-on: windows-latest - steps: - - uses: actions/checkout@v3 - with: - path: repo - - uses: actions/download-artifact@v3 - with: - name: windows-zip - - run: unzip jou.zip - - run: cp -r repo/tests repo/runtests.sh jou - shell: bash - - run: cd jou && ./jou.exe --verbose examples/hello.jou - shell: bash - - run: cd jou && ./runtests.sh --dont-run-make --verbose - shell: bash - compare-compilers: runs-on: windows-latest steps: - uses: actions/checkout@v3 - run: source activate && ./windows_setup.sh --small shell: bash + - run: source activate && mingw32-make self_hosted_compiler.exe + shell: bash + - run: source activate && ./self_hosted_compiler.exe -vv tests/should_succeed/union.jou + shell: bash - run: source activate && ./compare_compilers.sh shell: bash From 9b53f1322506af4f717bd2178af952a825074835 Mon Sep 17 00:00:00 2001 From: Akuli Date: Sun, 17 Dec 2023 01:38:23 +0200 Subject: [PATCH 04/10] is it even called? --- self_hosted/create_llvm_ir.jou | 3 +++ 1 file changed, 3 insertions(+) diff --git a/self_hosted/create_llvm_ir.jou b/self_hosted/create_llvm_ir.jou index ea9349c4..97c996d8 100644 --- a/self_hosted/create_llvm_ir.jou +++ b/self_hosted/create_llvm_ir.jou @@ -234,6 +234,9 @@ class AstToIR: # Makes a temporary pointer, places the value there, then casts and reads the pointer. def do_cast_through_pointers(self, value: LLVMValue*, to: LLVMType*) -> LLVMValue*: + printf("Cast through pointers!!!!!\n") + fflush(stdout) + p1 = LLVMBuildAlloca(self->builder, LLVMTypeOf(value), "cast_through_ptr_temp") LLVMBuildStore(self->builder, value, p1) p2 = LLVMBuildBitCast(self->builder, p1, LLVMPointerType(to, 0), "cast_through_ptr_temp") From 1750b4986ed10769b3be310bb3c57c9bf1b92867 Mon Sep 17 00:00:00 2001 From: Akuli Date: Sun, 17 Dec 2023 01:45:40 +0200 Subject: [PATCH 05/10] debug print --- self_hosted/typecheck.jou | 2 ++ 1 file changed, 2 insertions(+) diff --git a/self_hosted/typecheck.jou b/self_hosted/typecheck.jou index 9265f5a8..1a1cad0c 100644 --- a/self_hosted/typecheck.jou +++ b/self_hosted/typecheck.jou @@ -424,6 +424,8 @@ def handle_class_members_stage2(ft: FileTypes*, classdef: AstClassDef*) -> None: elif member->kind == AstClassMemberKind::Union: uid = union_id++ for k = 0; k < member->union_fields.nfields; k++: + printf("typecheck: set belongs_to_union = True\n") + fflush(stdout) type->class_members.fields = realloc(type->class_members.fields, (type->class_members.nfields + 1) * sizeof type->class_members.fields[0]) type->class_members.fields[type->class_members.nfields++] = ClassField{ name = member->union_fields.fields[k].name, From 1e65d71b6b46b57458a41df6efbbc6078c7276d3 Mon Sep 17 00:00:00 2001 From: Akuli Date: Sun, 17 Dec 2023 01:56:19 +0200 Subject: [PATCH 06/10] more debug print --- self_hosted/create_llvm_ir.jou | 7 +++++++ self_hosted/typecheck.jou | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/self_hosted/create_llvm_ir.jou b/self_hosted/create_llvm_ir.jou index 97c996d8..3548537b 100644 --- a/self_hosted/create_llvm_ir.jou +++ b/self_hosted/create_llvm_ir.jou @@ -588,6 +588,13 @@ class AstToIR: field = instance_type->class_members.find_field(ast->instantiation.field_names[i]) assert field != NULL value = self->do_expression(&ast->instantiation.field_values[i]) + printf("create_llvm_ir: (type=%s) get belongs_to_union = ", instance_type->name) + if field->belongs_to_union: + printf("True") + else: + printf("False") + printf(" for field '%s'\n", field->name) + fflush(stdout) if field_uses_i8_ptr_hack(field) or field->belongs_to_union: value = self->do_cast_through_pointers(value, LLVMStructGetTypeAtIndex(type_to_llvm(instance_type), field->union_id)) assert LLVMTypeOf(value) == LLVMStructGetTypeAtIndex(type_to_llvm(instance_type), field->union_id) diff --git a/self_hosted/typecheck.jou b/self_hosted/typecheck.jou index 1a1cad0c..86cef95f 100644 --- a/self_hosted/typecheck.jou +++ b/self_hosted/typecheck.jou @@ -424,7 +424,7 @@ def handle_class_members_stage2(ft: FileTypes*, classdef: AstClassDef*) -> None: elif member->kind == AstClassMemberKind::Union: uid = union_id++ for k = 0; k < member->union_fields.nfields; k++: - printf("typecheck: set belongs_to_union = True\n") + printf("typecheck: (type=%s) set belongs_to_union = True for field '%s'\n", type->name, member->union_fields.fields[k].name) fflush(stdout) type->class_members.fields = realloc(type->class_members.fields, (type->class_members.nfields + 1) * sizeof type->class_members.fields[0]) type->class_members.fields[type->class_members.nfields++] = ClassField{ From 82d953a3b3e1d1a162ba71ee0f4e8879fba91eac Mon Sep 17 00:00:00 2001 From: Akuli Date: Sun, 17 Dec 2023 02:01:28 +0200 Subject: [PATCH 07/10] more debug --- self_hosted/create_llvm_ir.jou | 2 ++ 1 file changed, 2 insertions(+) diff --git a/self_hosted/create_llvm_ir.jou b/self_hosted/create_llvm_ir.jou index 3548537b..0416fb6c 100644 --- a/self_hosted/create_llvm_ir.jou +++ b/self_hosted/create_llvm_ir.jou @@ -596,6 +596,8 @@ class AstToIR: printf(" for field '%s'\n", field->name) fflush(stdout) if field_uses_i8_ptr_hack(field) or field->belongs_to_union: + printf("IF STATEMENT\n") + fflush(stdout) value = self->do_cast_through_pointers(value, LLVMStructGetTypeAtIndex(type_to_llvm(instance_type), field->union_id)) assert LLVMTypeOf(value) == LLVMStructGetTypeAtIndex(type_to_llvm(instance_type), field->union_id) result = LLVMBuildInsertValue(self->builder, result, value, field->union_id, "instance") From cf71c001a9c41d41f3db6a2a22d4c679adbdc9a4 Mon Sep 17 00:00:00 2001 From: Akuli Date: Sun, 17 Dec 2023 02:01:53 +0200 Subject: [PATCH 08/10] even more debug --- self_hosted/create_llvm_ir.jou | 3 +++ 1 file changed, 3 insertions(+) diff --git a/self_hosted/create_llvm_ir.jou b/self_hosted/create_llvm_ir.jou index 0416fb6c..a0c26196 100644 --- a/self_hosted/create_llvm_ir.jou +++ b/self_hosted/create_llvm_ir.jou @@ -600,6 +600,9 @@ class AstToIR: fflush(stdout) value = self->do_cast_through_pointers(value, LLVMStructGetTypeAtIndex(type_to_llvm(instance_type), field->union_id)) assert LLVMTypeOf(value) == LLVMStructGetTypeAtIndex(type_to_llvm(instance_type), field->union_id) + else: + printf("ELSE STATEMENT\n") + fflush(stdout) result = LLVMBuildInsertValue(self->builder, result, value, field->union_id, "instance") elif ast->kind == AstExpressionKind::GetClassField: From 4f9f9dfeb294f345cc596ca4b2f8d7cc1a9797d0 Mon Sep 17 00:00:00 2001 From: Akuli Date: Sun, 17 Dec 2023 02:04:46 +0200 Subject: [PATCH 09/10] more debug --- self_hosted/create_llvm_ir.jou | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/self_hosted/create_llvm_ir.jou b/self_hosted/create_llvm_ir.jou index a0c26196..7f3df9b2 100644 --- a/self_hosted/create_llvm_ir.jou +++ b/self_hosted/create_llvm_ir.jou @@ -595,7 +595,12 @@ class AstToIR: printf("False") printf(" for field '%s'\n", field->name) fflush(stdout) - if field_uses_i8_ptr_hack(field) or field->belongs_to_union: + a = field_uses_i8_ptr_hack(field) + b = field->belongs_to_union + c = a or b + printf("abc = %d%d%d\n", a as int, b as int, c as int) + fflush(stdout) + if c: printf("IF STATEMENT\n") fflush(stdout) value = self->do_cast_through_pointers(value, LLVMStructGetTypeAtIndex(type_to_llvm(instance_type), field->union_id)) From 5288a464ae215daea8a1d29449df86466ee364c2 Mon Sep 17 00:00:00 2001 From: Akuli Date: Sun, 17 Dec 2023 02:10:35 +0200 Subject: [PATCH 10/10] disable optimizations? --- Makefile.windows | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.windows b/Makefile.windows index c6c8331a..aea9be2d 100644 --- a/Makefile.windows +++ b/Makefile.windows @@ -31,7 +31,7 @@ config.jou: echo " return NULL" >> config.jou self_hosted_compiler.exe: jou.exe config.jou $(wildcard self_hosted/*.jou) - ./jou.exe -o $@ --linker-flags "$(LDFLAGS)" self_hosted/main.jou + ./jou.exe -O0 -o $@ --linker-flags "$(LDFLAGS)" self_hosted/main.jou .PHONY: clean clean: