diff --git a/.gitignore b/.gitignore index 0d5327b2..1a0b1886 100644 --- a/.gitignore +++ b/.gitignore @@ -51,6 +51,9 @@ build/standalone *.ll *.bc +build/plots.txt +build/massive.flx + *.ir shakefile.hi shakefile @@ -65,6 +68,7 @@ bug-repro .vscode *.vcxproj.filters +*.vcxproj.user countloc.bat @@ -72,7 +76,7 @@ prof.svg prof_with_square.svg build/cmake-output .idea -CMakeLists.txt +# CMakeLists.txt source_old diff --git a/.travis.yml b/.travis.yml index 476530e2..5a9474db 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,6 +24,7 @@ addons: - libmpfr4 homebrew: packages: + - mpfr - llvm@7 update: true diff --git a/LICENSE b/LICENSE index 949d4db7..d427e51f 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ Apache License same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2015 zhiayang@gmail.com + Copyright 2015 zhiayang Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/appveyor.yml b/appveyor.yml index b85bac16..498db876 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,20 +1,20 @@ -image: Visual Studio 2017 +image: Visual Studio 2019 clone_folder: c:\projects\flax environment: global: - DEPS_DBG_INCLUDES_DIR: c:\projects\lib\mpir\DebugNoSyms\include;c:\projects\lib\mpfr\DebugNoSyms\include;c:\projects\lib\llvm\DebugNoSyms\include - DEPS_REL_INCLUDES_DIR: c:\projects\lib\mpir\Release\include;c:\projects\lib\mpfr\Release\include;c:\projects\lib\llvm\Release\include - DEPS_DBG_LIBS_DIR: c:\projects\lib\mpir\DebugNoSyms\lib;c:\projects\lib\mpfr\DebugNoSyms\lib;c:\projects\lib\llvm\DebugNoSyms\lib - DEPS_REL_LIBS_DIR: c:\projects\lib\mpir\Release\lib;c:\projects\lib\mpfr\Release\lib;c:\projects\lib\llvm\Release\lib + MPIR_ROOT_DIR: c:\projects\lib\mpir + MPFR_ROOT_DIR: c:\projects\lib\mpfr + LLVM_ROOT_DIR: c:\projects\lib\llvm + LIBFFI_ROOT_DIR: c:\projects\lib\libffi cache: - c:\projects\lib -> appveyor.yml install: # Set up the build environment - - cmd: call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" x64 + - cmd: call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvarsall.bat" x64 # download our deps. - ps: "[Net.ServicePointManager]::SecurityProtocol = 'Ssl3, Tls, Tls11, Tls12'" - ps: >- @@ -22,12 +22,22 @@ install: Invoke-WebRequest 'https://github.com/flax-lang/flax/releases/download/win-build-deps/libraries.zip' -OutFile 'c:\projects\libs.zip' 7z x -y -oc:\projects\lib c:\projects\libs.zip } + # Download ninja + - cmd: mkdir C:\ninja-build + - ps: (new-object net.webclient).DownloadFile('https://github.com/mesonbuild/cidata/raw/master/ninja.exe', 'C:\ninja-build\ninja.exe') + - cmd: set PYTHON_ROOT=C:\python37-x64 + # Add neccessary paths to PATH variable + - cmd: set PATH=%cd%;C:\ninja-build;%PYTHON_ROOT%;%PYTHON_ROOT%\Scripts;%PATH% + # Install meson + - cmd: pip install meson build_script: - - ps: msbuild -maxcpucount -verbosity:minimal -nologo -p:Configuration=Release c:\projects\flax\flax.vcxproj + - ps: meson --buildtype=release build/meson-rel + - ps: ninja -C build/meson-rel test_script: - ps: cd c:\projects\flax - ps: New-Item -Force -Path build\sysroot\usr\local\lib\flaxlibs -ItemType Directory - ps: Copy-Item -Recurse -Force libs\* build\sysroot\usr\local\lib\flaxlibs\ - - ps: cmd /c build\sysroot\windows\Release\flaxc.exe -sysroot build\sysroot -run build\tester.flx \ No newline at end of file + - ps: build\meson-rel\flaxc.exe -sysroot build\sysroot -run build\tester.flx > output.txt + - ps: cat output.txt diff --git a/build/expected_output.txt b/build/expected_output.txt deleted file mode 100644 index 7171e779..00000000 --- a/build/expected_output.txt +++ /dev/null @@ -1,255 +0,0 @@ - *** FIBONACII SEQUENCE *** ----------------------------------------- -1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181 - - - *** FIZZBUZZ *** ----------------------------------------- -00: FizzBuzz -01: 1 -02: 2 -03: Fizz -04: 4 -05: Buzz -06: Fizz -07: 7 -08: 8 -09: Fizz -10: Buzz -11: 11 -12: Fizz -13: 13 -14: 14 -15: FizzBuzz - - - - *** PRINTING INT LIMITS *** ----------------------------------------- -i8.min = -128 i8.max = 127 -i16.min = -32768 i16.max = 32767 -i32.min = -2147483648 i32.max = 2147483647 -i64.min = -9223372036854775808 i64.max = 9223372036854775807 - -u8.min = 0 u8.max = 255 -u16.min = 0 u16.max = 65535 -u32.min = 0 u32.max = 4294967295 -u64.min = 0 u64.max = 18446744073709551615 - - - -*** SCOPE RESOLUTION REGRESSION TEST *** ----------------------------------------- -gg.0: 4, gg.1: 100.39 -[400] -p: 15.707317, g: 4, m: 511, argv[0]: _llvm_jit__build/test, 3.14159265358979 - -x1: 3, x2: 4, x3: 23 -nested -tup: HELLO, WORLD! -t1 = 311, t2 = 9 -afoo: 311 - - - - *** OPERATOR/TUPLE REGRESSION TEST *** ----------------------------------------- -g is: 100 -m is [(1.000000, 2.000000, 3.000000), (4.000000, 5.000000, 6.000000)] -dot = 32.000000 -v: (4.000000, 8.000000, 13.000000) -v: (5.000000, 10.000000, 17.000000) -vv: (15.000000, 30.000000, 51.000000) -cross = (-3.000000, 6.000000, -3.000000) -crossn = (3.000000, -6.000000, 3.000000) -WRITE: 40 -READ -k: 31 -(31.00), vr = -5.00 -E -lol[2]: E // HELLO - - - - *** ARRAY REGRESSION TEST *** ----------------------------------------- -a[0][0]: 1, a[0][1]: 2, a[1][0]: 5, a[1][1]: 400 -d[0]: 1.000000, d[1]: 2.000000, d[2]: 4.000000, d[3]: 8.000000 -arr[1] = 97 -arr[2] = 43 - - -s[1][2] = BOO YOU STRING, s[1][2].length = 14 - -PRE X -POST X -k = 0x7fd49fc02a50, x = 0x7fd49fc02a50 -z <= x: 1 -z[0] = aaaAAA // 1 -z[1] = BBBbbb // 1 -z[2] = cccCCC // 1 -z[3] = DDDddd // 1 -z[4] = eeeEEE // 1 -z[5] = // 1 -z[6] = // 1 -z[7] = // 1 -z[8] = // 1 -z[9] = LAST ELEMENT // -1 -z.back() = LAST ELEMENT, length = 10, cap = 16 -sum = 120 - - - - *** GENERICS REGRESSION TEST *** ----------------------------------------- -foo: triple T -instance generic -extension generic -static extension generic -static generic -namespace generic - - - - *** FUNCTION REGRESSION TEST *** ----------------------------------------- -foo: 10 -bar: 20 -each test: 11 22 33 44 - -scope test -static bar in class: 10 -static bar in nested class: 20 -foo in namespace: 30 - -method test -foo in class: 0x7ffee53e3920 // 40 -foo in nested class: 0x7ffee53e3920 // 50 - -generic test - -class test -et: 300 -static: 0x7ffee53e3928, 500 -static: 0x7ffee53e3928, 600 - -generic member test -static generic: 10 -method generic: 0x7ffee53e3920 // 10 - -direct field calling -static: 0x7ffee53e3928, 10 - - - - *** DEFER SEMANTICS TEST *** ----------------------------------------- -calling foo 1 -calling foo 2 -calling foo 3 - - - - *** ANY SEMANTICS TEST *** ----------------------------------------- -k as string = foo -k as int = 301 -typeid(k): 6 -a = 10 -a = 631 -not large -bar = 6, qux = -1 -a = 631, b = 20, c = 5413/30, d = 40, e = 50 - - - - *** SLICES REGRESSION TEST *** ----------------------------------------- --- 2, 3, 5, 7, 11, 13, 17 -- -original: Hello, world! -slice: lo, wor - - - - *** DECOMPOSITION REGRESSION TEST *** ----------------------------------------- - - ->> tuple decomposition -x = 10, y = 0x7ffee53e38f0 -a = 'a', b = 30, c = 40 - ->> array decomposition -a = 2, b = 30, c = 0x10a82a120, d = 7, e = 11 (x.length = 4) -a = hello, b = 2, c = 3 - ->> string decomposition -a = 'I', b = 'n', c = ' ' -the event of unexpected shutdown, all personnel are to evacuate the area immediately. - - - - - *** FOR LOOP REGRESSION TEST *** ----------------------------------------- - - ->> C-style for loops -i = 1, k = 0, v = 30 -i = 2, k = 10, v = 27 -i = 3, k = 20, v = 24 -i = 4, k = 30, v = 21 - ->> ranges -r1.l = 0, r1.u = 3 -r2.l = 0, r2.u = 0 - -0: i = 0 -1: i = 1 -2: i = 2 -3: i = 3 - ->> for-in array -0: 1 / a -1: 2 / b -2: 3 / c -3: 4 / d - ->> for-in string -0: 'w', 1: 'o', 2: 'o', 3: 'h', 4: 'o', 5: 'o', - - - - *** MISCELLANEOUS TESTS *** ----------------------------------------- -(10, 41) -10 -30 -40 -50 -60 -hi: [1, 2, 3, 4] -0, (40, hello, 40, hello) -439, 67 -'4' is digit: 1 -terminal size: 0x0 (chars), 0x0 (pixels) -test = 0x10a82674b 10 -(if-let) k > 27 -f.x = 10, b.c = 3, fo = 40 -k = 40 -s = some string broken over multiple lines -40, 3.0001410 -> 16 / 16 / 8 < -ta == tb: 1 -ints: [10, 20, 30, 40, 50, 60] - - - -============ TESTS COMPLETE ============ - - - - - - -<< done >> diff --git a/build/gltest.flx b/build/gltest.flx index 0f2d5918..e864e48c 100644 --- a/build/gltest.flx +++ b/build/gltest.flx @@ -1,5 +1,5 @@ // gltest.flx -// Copyright (c) 2014 - 2016, zhiayang@gmail.com +// Copyright (c) 2014 - 2016, zhiayang // Licensed under the Apache License Version 2.0. // import Foundation diff --git a/build/plots.txt b/build/plots.txt deleted file mode 100644 index 5b8cc46f..00000000 --- a/build/plots.txt +++ /dev/null @@ -1,33 +0,0 @@ -reps firs total lexer parser typecheck codegen -6183 50277 88.3 6.9 9.5 45.4 26.4 -12359 100133 174.9 12.7 19.8 92.1 50.3 -18535 149989 267.1 18.0 26.3 143.9 78.9 -24711 199845 359.3 26.3 36.3 186.8 109.9 -30887 249701 429.1 29.6 42.8 225.1 131.6 -37063 299557 522.5 36.0 55.4 271.3 159.8 -43239 349413 613.8 41.9 61.6 325.9 184.3 -49415 399269 705.1 50.8 71.1 369.8 213.4 -55591 449125 774.5 56.2 78.6 403.3 236.4 -61767 498981 883.5 69.4 91.8 448.9 273.3 -67943 548837 981.8 65.4 97.0 499.1 320.3 -74119 598693 1048.9 71.9 107.2 533.4 336.5 -80295 648549 1164.7 82.7 124.2 613.5 344.3 -86471 698405 1246.9 89.3 135.0 648.1 374.4 -92647 748261 1328.9 94.4 140.6 698.0 395.9 -98823 798117 1393.1 97.9 143.8 723.6 427.8 -104999 847973 1506.9 109.5 159.4 782.7 455.3 -111175 897829 1584.1 113.9 167.9 814.9 487.4 -117351 947685 1701.1 118.5 178.2 875.0 529.4 -123527 997541 1780.1 126.6 186.7 914.7 552.0 -129703 1047397 1884.4 132.1 196.8 939.5 616.1 -135879 1097253 1984.1 140.8 213.8 998.3 631.2 -142055 1147109 2062.6 146.6 215.3 1026.0 674.7 -148231 1196965 2152.9 150.8 225.0 1091.8 685.4 -154407 1246821 2241.0 162.6 236.8 1152.5 689.1 -160583 1296677 2313.7 168.0 236.2 1204.2 705.3 -166759 1346533 2406.2 175.8 255.9 1245.3 729.1 -172935 1396389 2469.1 176.3 255.3 1288.7 748.8 -179111 1446245 2585.6 184.3 271.9 1352.1 777.3 -185287 1496101 2670.8 191.0 277.5 1394.5 807.7 -191463 1545957 2754.9 199.0 287.5 1438.6 829.8 -197639 1595813 2843.1 203.8 299.2 1474.1 866.1 \ No newline at end of file diff --git a/build/run-test.bat b/build/run-test.bat index 08bb2ee5..bad4f2f3 100644 --- a/build/run-test.bat +++ b/build/run-test.bat @@ -17,7 +17,7 @@ IF /I "%1"=="debugopt" ( ) -ninja -C %buildDir% && cls && %buildDir%\flaxc.exe -Ox -sysroot build\sysroot -run build\%2.flx %3 %4 +ninja -C %buildDir% && cls && %buildDir%\flaxc.exe -Ox -sysroot build\sysroot -run build\%2.flx %3 %4 %5 %6 %7 %8 %9 IF /I "%1"=="release" ( copy %buildDir%\flaxc.exe build\sysroot\usr\local\bin\ >NUL diff --git a/build/supertiny.flx b/build/supertiny.flx index c7578c58..eede9588 100644 --- a/build/supertiny.flx +++ b/build/supertiny.flx @@ -1,5 +1,5 @@ // supertiny.flx -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. export supertiny @@ -34,18 +34,46 @@ import std::opt struct foo { x: int - y: str } @entry fn main() { + // do { + // var addr: ipv4 + // addr.raw3 = 0xff01a8c0; + + // printf("%d.%d.%d.%d\n", addr.bytes[0], addr.bytes[1], addr.bytes[2], addr.bytes[3]); + // } + do { - var addr: ipv4 - addr.raw3 = 0xff01a8c0; + var x: int + var y: int - printf("%d.%d.%d.%d\n", addr.bytes[0], addr.bytes[1], addr.bytes[2], addr.bytes[3]); + (x, y) = (30, 40) + (x, y) = (y, x) + + printf("%d, %d\n", x, y) } + // fn foo(a: T) -> T + // { + // return a + // } + + // do { + // // let q = foo(x: 10) + // printf("hello, world!\n") + + // let q = foo!(10) + // } + + // fn qux() -> int => 30 + + // let x = std::opt::some!(10) + // let f = foo!(3) + // let q = qux!() + + /* do { var x: @raw union { @@ -61,12 +89,10 @@ struct foo } - - /* import std::opt -import std:: map +import std::map var map: std::map! map.insert(3, "foo") @@ -78,58 +104,15 @@ import std:: map let res = map.remove(3) libc::printf("map[3]: res = %d, is_none = %d\n", res, map.search(3) is std::opt::none) - */ +*/ /* - !!! bugs (2) !!! - !!! 22/03/19 - { - 1. we currently check whether you are trying to put a refcounted type as a field of a raw union. however! - you can circumvent this check if you nest the field into inner types, or if you use transparent fields!! - } - - - ! bugs !!! - ! 02/12 - { - // 1. implicit method calling don't seem to work any more... strangely. done a cursory search for our - // 'SELF HANDLING' markers, but i can't seem to find the actual place where we *insert* the argument?? - // only where we insert the /parameter/ ... - - // i think this is less of a bug and more of an omission when we first started taking out the - // haphazard self-handling. - - // 2. see anytest.flx -- the last segment, with (x as Large).c has some kind of corruption. - // we're getting bogus values on debug builds of the compiler?? which means there's some kind of UB going on - // in the compiler itself... - - // expected: a = 631, b = 20, c = 173/30, d = 40, e = 50 - // actual: a = 631, b = 20, c = 173/-572662307, d = 40, e = 50 - - // note: it's 0xDDDDDDDD in hex, and according to The Internet (tm), that marks FREED MEMORY! - - // 3. after we enabled referring to types by name in any scope (see typecheck/variable.cpp), we have an issue because - // they're in the same namespace! we give super suboptimal error messages when we try to use a type as a value, or - // when we name a variable the same as a type (ie trying to use a value as a type!) - - 4. we should not be able to pass a mapping to the variant of a union, that's a bit weird. - eg: std::opt::some! works, which is a little strange. - edit: it only works in certain circumstances??? also if we use a constructor (like this: std::opt::some!(10)), the thing - get treated like a function call (and so finds no functions!) - - // 5. nested functions 'capture' the scope at which they are defined -- including variables! - // this is Bad News(tm) because we don't support closures, but the typechecker (and codegen) sees the variable 'in scope', and doesn't - // complain. need to figure out a way around this!!! - - } - - ! more things. - ! caa 03/10 + ! bugs ! { 2. splatting tuples into a generic function *will not work* - 4. pass the overall location to the polymorph solver. + 3. pass the overall location to the polymorph solver. } @@ -143,14 +126,6 @@ import std:: map - do we duplicate the context? - if not then??? } - // 12. allow generic solver to infer things from generic types passed in { - // eg: fn foobar(a: Qux, b: Qux) => ... - // foobar(Qux(), Qux()) should infer that A = i64 and B = str. - // } - 15. builtin way to get a character literal { - even just enforcing that we get either a string literal of exactly length 1, or an i8 type, at compile-time. - sure, we could just abort when passed a slice != length 1, but then why bother being statically typed?? - } 16. operator overload errors { something like this: @@ -162,61 +137,62 @@ import std:: map ofc potential candidates should be limited to ones where at least one of the types are similar. } -*/ + 17. better errors for ref-counting legality { + when throwing an error due to having a ref-counted type in unsupported places (like in a raw-union), we + should let the compiler show which member made the struct refcounted (because having a RC field in a struct + makes that struct RC), since the 'root cause' could be nested very deep in the type hierarchy somewhere. + eg. + error: reference-counted type 'foo' cannot be a member of a raw-union: + 39 | bar: foo + note: 'foo' inherits reference-counting from indirectly containing a field with a reference-counted type: + 303 | blabla: string + note: this is the type hierarchy leading from 'foo' leading to the type in question: + (foo <- qux <- some_type_t <- ... <- some_struct <- string) + of course it would be nice if we can selectively print more context and omit stuff in the middle, like this: + 10 | struct some_struct { + ... | ... + 391 | blabla: string + | + something like that. detecting indentation would be a little tricky if we want to correctly indent the + '...' that would appear in the struct body. + actually having a proper API to set up this kind of error message would be a tough one as well... + } + + 18. figure out how constructors should work with transparent fields { -// fn overloadErrorTest() -// { -// fn printThings(mul: f64, flts: [f64: ...]) -// { -// printf("floats: "); -// for i in flts { printf("%.1f ", mul * i) } + I think it's currently not worth the effort to support initialising transparent fields in constructors; there's + way too much stuff that needs to change. some findings: -// printf("\n") -// } + to actually allow it to pass typechecking, we just need to pass the "extended set" of field names to + `verifyStructConstructorArguments`, which ought to suffice. problem is, we call that from the polymorph + engine as well, and that's something i don't really feel like touching at the moment. -// fn printThings(mul: str, flts: [f64: ...]) -// { -// printf("floats: "); -// for i in flts { printf("%s: %.1f ", mul.ptr, i) } + ^ as a corollary to that, we also need a way to discover all the transparent fields that are accessible at any + point. currently in typecheck/dotop, we currently do an ad-hoc kind of thing, where we search for a particular + name that's needed. should be not-that-difficult to get a list of all accessible things, i hope. -// printf("\n") -// } + ^ we might need to duplicate this to work with ast::StructDefn as well, since the polymorph engine also uses + the same verifyStructConstructorArguments to instantiate types a-la function calls. -// fn printThings() -// { -// printf("print things 3\n") -// } + we also need some way to actually insert the value into the struct when it's being constructed, which might also + get complicated. -// do { -// printThings(30, ...[as f64: 1, 2, 3, 4, 5 ]) -// printThings(0.813, 1, 2, 4.41, 39, 491.3, 381.7) -// } -// } + TL;DR: too complicated, not worth the time currently. + } +*/ -// var glob: any -// fn test_refcounting_problem_thing() -// { -// fn foo(x: any) -// { -// if(x is int) -// { -// glob = x -// } -// } -// foo(30) -// } diff --git a/build/tester.flx b/build/tester.flx index 75a7fad9..4dfcc152 100644 --- a/build/tester.flx +++ b/build/tester.flx @@ -1,10 +1,11 @@ // tester.flx -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. export tester import libc +import std::io import std::limits import "tests/fizzbuzz.flx" @@ -49,143 +50,135 @@ fn runTests() let endTitle = "============ TESTS COMPLETE ============\n" - libc::printf("%s%s", fibTitle, thinLine) + std::io::print("%%", fibTitle, thinLine) do { var n = 1 while n < 20 { - libc::printf("%d", test_fib::doRecursiveFib(n)) + std::io::print("%", test_fib::doRecursiveFib(n)) n += 1 if n != 20 { - libc::printf(", ") + std::io::print(", ") } } - libc::printf("\n\n\n") + std::io::print("\n\n\n") } // fizzbuzz - libc::printf("%s%s", fizzbuzzTitle, thinLine) + std::io::print("%%", fizzbuzzTitle, thinLine) test_fizz::doFizzBuzz(15) - libc::printf("\n\n\n") + std::io::print("\n\n\n") // int limits - libc::printf("%s%s", intLimitsTitle, thinLine) + std::io::print("%%", intLimitsTitle, thinLine) test_limits::printIntegerLimits() - libc::printf("\n\n\n") + std::io::print("\n\n\n") // scopes - libc::printf("%s%s", scopeTitle, thinLine) + std::io::print("%%", scopeTitle, thinLine) test_scopes::doScopeTest("__llvm_jit__build/test") - libc::printf("\n\n\n") + std::io::print("\n\n\n") // operators and tuples (vectors type, mainly) - libc::printf("%s%s", operatorTitle, thinLine) + std::io::print("%%", operatorTitle, thinLine) // doOperatorTupleTest() - libc::printf("\n\n\n") + std::io::print("\n\n\n") // arrays - libc::printf("%s%s", arrayTitle, thinLine) + std::io::print("%%", arrayTitle, thinLine) test_arrays::doArrayTest() - libc::printf("\n\n\n") + std::io::print("\n\n\n") // generics - libc::printf("%s%s", genericsTitle, thinLine) + std::io::print("%%", genericsTitle, thinLine) test_generics::doGenericsTest() - libc::printf("\n\n\n") + std::io::print("\n\n\n") // classes - libc::printf("%s%s", classTitle, thinLine) + std::io::print("%%", classTitle, thinLine) test_classes::doClassTest() - libc::printf("\n\n\n") + std::io::print("\n\n\n") // first-class-functions - libc::printf("%s%s", functionsTitle, thinLine) + std::io::print("%%", functionsTitle, thinLine) test_functions::doFunctionTest() - libc::printf("\n\n\n") + std::io::print("\n\n\n") // defer semantics - libc::printf("%s%s", deferTitle, thinLine) + std::io::print("%%", deferTitle, thinLine) test_defer::doDeferTest() - libc::printf("\n\n\n") + std::io::print("\n\n\n") // any - libc::printf("%s%s", anyTitle, thinLine) + std::io::print("%%", anyTitle, thinLine) test_any::doAnyTest() - libc::printf("\n\n\n") + std::io::print("\n\n\n") // slices - libc::printf("%s%s", slicesTitle, thinLine) + std::io::print("%%", slicesTitle, thinLine) test_slices::doSlicesTest() - libc::printf("\n\n\n") + std::io::print("\n\n\n") // decomposition - libc::printf("%s%s", decomposeTitle, thinLine) + std::io::print("%%", decomposeTitle, thinLine) test_decomposition::doDecompositionTest() - libc::printf("\n\n\n") + std::io::print("\n\n\n") // for-loops - libc::printf("%s%s", forLoopTitle, thinLine) + std::io::print("%%", forLoopTitle, thinLine) test_forloops::doForLoopTest() - libc::printf("\n\n\n") + std::io::print("\n\n\n") // linked-list (generics) - libc::printf("%s%s", linkedListTitle, thinLine) + std::io::print("%%", linkedListTitle, thinLine) test_linkedlist::doLinkedListTest() - libc::printf("\n\n\n") + std::io::print("\n\n\n") // unions (generics) - libc::printf("%s%s", unionsTitle, thinLine) + std::io::print("%%", unionsTitle, thinLine) test_unions::doUnionsTest() - libc::printf("\n\n\n") + std::io::print("\n\n\n") // using - libc::printf("%s%s", usingTitle, thinLine) + std::io::print("%%", usingTitle, thinLine) test_using::doUsingTest() - libc::printf("\n\n\n") + std::io::print("\n\n\n") // misc tests - libc::printf("%s%s", miscTitle, thinLine) + std::io::print("%%", miscTitle, thinLine) // miscellaneousTests() - libc::printf("\n\n\n") - - + std::io::print("\n\n\n") // fin. - libc::printf("%s\n\n\n\n\n", endTitle) + std::io::print("%\n\n\n\n\n", endTitle) } - - -// name idea: mill - - - -@entry fn main(argc: i32, argv: &&i8) -> int +@entry fn main() -> int { runTests() - libc::printf("\n<< done >>\n") + std::io::print("\n<< done >>\n") return 0 } diff --git a/build/tests/anytest.flx b/build/tests/anytest.flx index b4aacff4..09df4411 100644 --- a/build/tests/anytest.flx +++ b/build/tests/anytest.flx @@ -1,5 +1,5 @@ // anytest.flx -// Copyright (c) 2014 - 2016, zhiayang@gmail.com +// Copyright (c) 2014 - 2016, zhiayang // Licensed under the Apache License Version 2.0. export test_any diff --git a/build/tests/arraytest.flx b/build/tests/arraytest.flx index fe061814..3923be45 100644 --- a/build/tests/arraytest.flx +++ b/build/tests/arraytest.flx @@ -1,5 +1,5 @@ // arraytest.flx -// Copyright (c) 2014 - 2015, zhiayang@gmail.com +// Copyright (c) 2014 - 2015, zhiayang // Licensed under the Apache License Version 2.0. export test_arrays diff --git a/build/tests/classes.flx b/build/tests/classes.flx index 5b63fa64..a5d4831b 100644 --- a/build/tests/classes.flx +++ b/build/tests/classes.flx @@ -1,5 +1,5 @@ // classes.flx -// Copyright (c) 2017, zhiayang@gmail.com +// Copyright (c) 2017, zhiayang // Licensed under the Apache License Version 2.0. export test_classes diff --git a/build/tests/decomposition.flx b/build/tests/decomposition.flx index fe7eaf1a..76e90d29 100644 --- a/build/tests/decomposition.flx +++ b/build/tests/decomposition.flx @@ -1,5 +1,5 @@ // decomposition.flx -// Copyright (c) 2014 - 2016, zhiayang@gmail.com +// Copyright (c) 2014 - 2016, zhiayang // Licensed under the Apache License Version 2.0. export test_decomposition diff --git a/build/tests/defertest.flx b/build/tests/defertest.flx index b6048843..234a005c 100644 --- a/build/tests/defertest.flx +++ b/build/tests/defertest.flx @@ -1,5 +1,5 @@ // defer.flx -// Copyright (c) 2014 - 2016, zhiayang@gmail.com +// Copyright (c) 2014 - 2016, zhiayang // Licensed under the Apache License Version 2.0. export test_defer diff --git a/build/tests/fizzbuzz.flx b/build/tests/fizzbuzz.flx index 2934ad56..0d301f86 100644 --- a/build/tests/fizzbuzz.flx +++ b/build/tests/fizzbuzz.flx @@ -1,5 +1,5 @@ // fizzbuzz.flx -// Copyright (c) 2014 - 2015, zhiayang@gmail.com +// Copyright (c) 2014 - 2015, zhiayang // Licensed under the Apache License Version 2.0. export test_fizz diff --git a/build/tests/forloops.flx b/build/tests/forloops.flx index 9953c80d..e2dbe729 100644 --- a/build/tests/forloops.flx +++ b/build/tests/forloops.flx @@ -1,5 +1,5 @@ // forloops.flx -// Copyright (c) 2014 - 2016, zhiayang@gmail.com +// Copyright (c) 2014 - 2016, zhiayang // Licensed under the Apache License Version 2.0. export test_forloops diff --git a/build/tests/functions.flx b/build/tests/functions.flx index 70509d3c..63de5b27 100644 --- a/build/tests/functions.flx +++ b/build/tests/functions.flx @@ -1,5 +1,5 @@ // functions.flx -// Copyright (c) 2014 - 2016, zhiayang@gmail.com +// Copyright (c) 2014 - 2016, zhiayang // Licensed under the Apache License Version 2.0. export test_functions diff --git a/build/tests/generics.flx b/build/tests/generics.flx index 95e22a6b..b4b83611 100644 --- a/build/tests/generics.flx +++ b/build/tests/generics.flx @@ -1,5 +1,5 @@ // generics.flx -// Copyright (c) 2014 - 2015, zhiayang@gmail.com +// Copyright (c) 2014 - 2015, zhiayang // Licensed under the Apache License Version 2.0. export test_generics diff --git a/build/tests/intlimits.flx b/build/tests/intlimits.flx index 8c74a023..d101b4d4 100644 --- a/build/tests/intlimits.flx +++ b/build/tests/intlimits.flx @@ -1,5 +1,5 @@ // intlimits.flx -// Copyright (c) 2014 - 2016, zhiayang@gmail.com +// Copyright (c) 2014 - 2016, zhiayang // Licensed under the Apache License Version 2.0. export test_limits diff --git a/build/tests/linkedlist.flx b/build/tests/linkedlist.flx index 9e2ac2be..6299c7f4 100644 --- a/build/tests/linkedlist.flx +++ b/build/tests/linkedlist.flx @@ -1,5 +1,5 @@ // linkedlist.flx -// Copyright (c) 2017, zhiayang@gmail.com +// Copyright (c) 2017, zhiayang // Licensed under the Apache License Version 2.0. export test_linkedlist diff --git a/build/tests/misc.flx b/build/tests/misc.flx index ab77f472..54f72a2b 100644 --- a/build/tests/misc.flx +++ b/build/tests/misc.flx @@ -1,5 +1,5 @@ // misc.flx -// Copyright (c) 2014 - 2016, zhiayang@gmail.com +// Copyright (c) 2014 - 2016, zhiayang // Licensed under the Apache License Version 2.0. diff --git a/build/tests/misc/bar.flx b/build/tests/misc/bar.flx deleted file mode 100644 index 933b10ef..00000000 --- a/build/tests/misc/bar.flx +++ /dev/null @@ -1,11 +0,0 @@ -// bar.flx -// Copyright (c) 2017, zhiayang@gmail.com -// Licensed under the Apache License Version 2.0. - -export bar -public import "libc" - -public fn bar() -{ - libc::printf("hello, world!\n") -} \ No newline at end of file diff --git a/build/tests/misc/foo.flx b/build/tests/misc/foo.flx deleted file mode 100644 index d820b290..00000000 --- a/build/tests/misc/foo.flx +++ /dev/null @@ -1,11 +0,0 @@ -// foo.flx -// Copyright (c) 2017, zhiayang@gmail.com -// Licensed under the Apache License Version 2.0. - -import "qux" - -fn main() -{ - qux::bar() - qux::libc::printf("hello!\n") -} \ No newline at end of file diff --git a/build/tests/misc/qux.flx b/build/tests/misc/qux.flx deleted file mode 100644 index 2b39da03..00000000 --- a/build/tests/misc/qux.flx +++ /dev/null @@ -1,7 +0,0 @@ -// qux.flx -// Copyright (c) 2017, zhiayang@gmail.com -// Licensed under the Apache License Version 2.0. - -export qux -public import "bar" as _ - diff --git a/build/tests/operators.flx b/build/tests/operators.flx index 09284f9d..832458ad 100644 --- a/build/tests/operators.flx +++ b/build/tests/operators.flx @@ -1,5 +1,5 @@ // operators.flx -// Copyright (c) 2014 - 2016, zhiayang@gmail.com +// Copyright (c) 2014 - 2016, zhiayang // Licensed under the Apache License Version 2.0. import Math diff --git a/build/tests/recursiveFib.flx b/build/tests/recursiveFib.flx index eb209aa8..14991f23 100644 --- a/build/tests/recursiveFib.flx +++ b/build/tests/recursiveFib.flx @@ -1,5 +1,5 @@ // recursiveFib.flx -// Copyright (c) 2014 - 2015, zhiayang@gmail.com +// Copyright (c) 2014 - 2015, zhiayang // Licensed under the Apache License Version 2.0. export test_fib diff --git a/build/tests/scopes.flx b/build/tests/scopes.flx index 91dad92d..6390f38a 100644 --- a/build/tests/scopes.flx +++ b/build/tests/scopes.flx @@ -1,5 +1,5 @@ // scopes.flx -// Copyright (c) 2014 - 2015, zhiayang@gmail.com +// Copyright (c) 2014 - 2015, zhiayang // Licensed under the Apache License Version 2.0. export test_scopes @@ -8,7 +8,7 @@ import libc as _ import std::math -@operator prefix 900 √ +@operator[prefix, 900, √] operator prefix √ (x: f64) -> f64 { return math::sqrt(x) } @@ -139,6 +139,10 @@ public fn doScopeTest(argv: str) let t1 = n1::n2::m.foo.somefoo().bar let t2 = n1::n2::nest().0.length + //! hello morning me + //* this thing below crashes the ffi-printf + //* we're not setting the string properly, somehow. + //* investigate!!!! n1::n2::tup.0 = "HELLO, WORLD!" //string("HELLO, WORLD") + "!" printf("tup: %s\n", n1::n2::tup.0) diff --git a/build/tests/slices.flx b/build/tests/slices.flx index 6f7ffc87..ea3c7c0d 100644 --- a/build/tests/slices.flx +++ b/build/tests/slices.flx @@ -1,5 +1,5 @@ // slices.flx -// Copyright (c) 2014 - 2016, zhiayang@gmail.com +// Copyright (c) 2014 - 2016, zhiayang // Licensed under the Apache License Version 2.0. export test_slices diff --git a/build/tests/unions.flx b/build/tests/unions.flx index 35f9aa96..4e508173 100644 --- a/build/tests/unions.flx +++ b/build/tests/unions.flx @@ -1,5 +1,5 @@ // unions.flx -// Copyright (c) 2017, zhiayang@gmail.com +// Copyright (c) 2017, zhiayang // Licensed under the Apache License Version 2.0. export test_unions @@ -11,6 +11,26 @@ union option none } +@raw union ipv4 +{ + _: struct { + _: @raw union { + bytes2: [u8: 4] + raw3: u32 + } + } + + _: struct { + bytes: [u8: 4] + } + + _: struct { + raw2: u32 + } + + raw: u32 +} + public fn doUnionsTest() { do { @@ -35,6 +55,17 @@ public fn doUnionsTest() printf("q = %s, v = %d\n", q as Bar::some, v as Bar::other) } + + do { + var addr: ipv4 + addr.raw3 = 0xff01a8c0; + printf("%d.%d.%d.%d\n", addr.bytes[0], addr.bytes[1], addr.bytes[2], addr.bytes[3]); + } } + + + + + diff --git a/build/tests/using.flx b/build/tests/using.flx index ae06b32b..b5d4eb2e 100644 --- a/build/tests/using.flx +++ b/build/tests/using.flx @@ -1,5 +1,5 @@ // using.flx -// Copyright (c) 2017, zhiayang@gmail.com +// Copyright (c) 2017, zhiayang // Licensed under the Apache License Version 2.0. export test_using diff --git a/build/ultratiny.flx b/build/ultratiny.flx new file mode 100644 index 00000000..a374b771 --- /dev/null +++ b/build/ultratiny.flx @@ -0,0 +1,51 @@ +// ultratiny.flx +// Copyright (c) 2019, zhiayang +// Licensed under the Apache License Version 2.0. + +export ultratiny + +import libc +import std::math + + +var value = 401.0 + +#if value > 30.0 +{ + #run { + value = 30 + libc::printf("true case\n") + } +} +else +{ + #run libc::printf("false case\n") +} + +#run { + value = math::sqrt(value) +} + +@entry fn main() +{ + let k = 30 + let x = #run math::sqrt(value) + libc::printf("sqrt(%.1f) = %.3f\n", value, x) +} + + + + + + + + + + + + + + + + + diff --git a/build/unity-build.ps1 b/build/unity-build.ps1 deleted file mode 100644 index 4b6fc9aa..00000000 --- a/build/unity-build.ps1 +++ /dev/null @@ -1,179 +0,0 @@ -# meant to be executed from flax - -$BuildType = $args[0] -$TestProg = $args[1] - -if ([String]::IsNullOrWhiteSpace($BuildType)) { $BuildType = "Debug" } -if ([String]::IsNullOrWhiteSpace($TestProg)) { $TestProg = "tester" } - -echo "Building $BuildType" - -$projectDir = "D:\Projects\flax" -$objectDir = "$projectDir\build\sysroot\windows\obj\x64\$BuildType" -$outputDir = "$projectDir\build\sysroot\windows\$BuildType" - -$llvmDir = "D:\Projects\lib\llvm\$BuildType" -$mpirDir = "D:\Projects\lib\mpir\$BuildType" -$mpfrDir = "D:\Projects\lib\mpfr\$BuildType" - -$llvmLibDir = "$llvmDir\lib" -$llvmIncludeDir = "$llvmDir\include" - -$mpfrLibDir = "$mpfrDir\lib" -$mpfrIncludeDir = "$mpfrDir\include" - -$mpirLibDir = "$mpirDir\lib" -$mpirIncludeDir = "$mpirDir\include" - - -if (Test-Path "$projectDir\source\unity.cpp") { - Remove-Item "$projectDir\source\unity.cpp" -} - - -$fileList = (Get-ChildItem "$projectDir\source" -File -Name -Recurse -Filter *.cpp) - - -$includeList = @" -#define NOMINMAX -#define WIN32_LEAN_AND_MEAN 1 -#include - -#include "platform.h" - -#undef DO_NOT_INCLUDE_PRECOMPILE -#include "precompile.h" - -#define DO_NOT_INCLUDE_PRECOMPILE - -"@ - -Foreach ($file in $fileList) { - $includeList += "#include `"$file`"`n" -} - -# ok, so what we want to do is to make a file. -New-Item -Path source/ -Force -Name "unity.cpp" -Value $includeList | Out-Null - - - -# after we're done with that, we need to compile utf8rewind... sigh. - -New-Item -Path $objectDir\utf8rewind -ItemType Directory -Force | Out-Null -New-Item -Path $outputDir -ItemType Directory -Force | Out-Null - -(Get-ChildItem "$projectDir\source" -File -Name -Recurse -Filter *.c) | ForEach-Object { - $basename = [io.path]::GetFileNameWithoutExtension($_) - cl.exe /nologo /c /EHsc source\$_ /Fo$objectDir\utf8rewind\$basename.obj -} - -# ok, next link a library. -$utf8Objs = (Get-ChildItem "$objectDir\utf8rewind" -File -Name -Filter *.obj) | ForEach-Object { - "$objectDir\utf8rewind\" + $_ -} - -lib.exe /nologo /out:$projectDir\build\sysroot\windows\obj\x64\$BuildType\utf8rewind.lib $utf8Objs - - -$linkLibraries = @( - "LLVMCore.lib", - "LLVMSupport.lib", - "LLVMTarget.lib", - "LLVMPasses.lib", - "LLVMAnalysis.lib", - "LLVMGlobalISel.lib", - "LLVMLibDriver.lib", - "LLVMLinker.lib", - "LLVMipo.lib", - "LLVMBinaryFormat.lib", - "LLVMMC.lib", - "LLVMMCJIT.lib", - "LLVMMCParser.lib", - "LLVMMCDisassembler.lib", - "LLVMObject.lib", - "LLVMScalarOpts.lib", - "LLVMVectorize.lib", - "LLVMCodegen.lib", - "LLVMTablegen.lib", - "LLVMBitReader.lib", - "LLVMBitWriter.lib", - "LLVMInstrumentation.lib", - "LLVMRuntimeDyld.lib", - "LLVMInstCombine.lib", - "LLVMInterpreter.lib", - "LLVMExecutionEngine.lib", - "LLVMSelectionDAG.lib", - "LLVMTransformUtils.lib", - "LLVMDebugInfoCodeView.lib", - "LLVMDebugInfoDWARF.lib", - "LLVMDebugInfoMSF.lib", - "LLVMDebugInfoPDB.lib", - "LLVMAsmPrinter.lib", - "LLVMX86AsmPrinter.lib", - "LLVMProfileData.lib", - "LLVMX86AsmParser.lib", - "LLVMX86Info.lib", - "LLVMX86CodeGen.lib", - "LLVMX86Utils.lib", - "LLVMX86Desc.lib", - "LLVMDlltoolDriver.lib", - "mpfr.lib", - "mpir.lib", - "$objectDir\utf8rewind.lib" -) - - -$compileFlags = @( - "/nologo", - "/GL", - "/wd4624", - "/wd4291", - "/wd4141", - "/wd4996", - "/EHsc", - "/std:c++17", - "/I$projectDir\source\include", - "/I$llvmIncludeDir", - "/I$mpirIncludeDir", - "/I$mpfrIncludeDir", - "/Fe$outputDir\flax-unity.exe", - "$projectDir\source\unity.cpp" -) - -If ($BuildType -eq "Release") -{ - $optimisationFlags = @( - "/O2", - "/MD" - ) -} -Else -{ - $optimisationFlags = @( - "/Od", - "/MDd", - "/Debug:fastlink" - ) -} - -$linkerFlags = @( - "/MACHINE:X64", - "/NODEFAULTLIB:LIBCMTD.lib", - "/NODEFAULTLIB:LIBCMT.lib", - "/LIBPATH:$llvmLibDir", - "/LIBPATH:$mpfrLibDir", - "/LIBPATH:$mpirLibDir" -) - -cl.exe @compileFlags $optimisationFlags /link $linkerFlags $linkLibraries - - -# copy the flax libraries. -# robocopy $projectDir\libs $projectDir\build\sysroot\usr\local\lib\flaxlibs /e /nfl /ndl /njh /njs /nc /ns /np - -echo `n`n - -# ok, now run flax. - -$flaxArgs = @("-sysroot", "$projectDir\build\sysroot", "-run", "$projectDir\build\$TestProg.flx") -& $outputDir\flax-unity.exe $flaxArgs \ No newline at end of file diff --git a/external/tinyprocesslib/process.cpp b/external/tinyprocesslib/process.cpp index 459cc860..fd1517e8 100644 --- a/external/tinyprocesslib/process.cpp +++ b/external/tinyprocesslib/process.cpp @@ -1,4 +1,4 @@ -#include "process.h" +#include "tinyprocess.h" namespace tinyproclib { Process::Process(const string_type &command, const string_type &path, diff --git a/external/tinyprocesslib/process_unix.inc b/external/tinyprocesslib/process_unix.inc index 5b9793c5..3d68552b 100644 --- a/external/tinyprocesslib/process_unix.inc +++ b/external/tinyprocesslib/process_unix.inc @@ -1,4 +1,4 @@ -#include "process.h" +#include "tinyprocess.h" #include #include #include diff --git a/external/tinyprocesslib/process_win.inc b/external/tinyprocesslib/process_win.inc index b1d598f3..3dcd1de2 100644 --- a/external/tinyprocesslib/process_win.inc +++ b/external/tinyprocesslib/process_win.inc @@ -1,4 +1,4 @@ -#include "process.h" +#include "tinyprocess.h" #include #include #include diff --git a/external/tinyprocesslib/process.h b/external/tinyprocesslib/tinyprocess.h similarity index 100% rename from external/tinyprocesslib/process.h rename to external/tinyprocesslib/tinyprocess.h index c547d762..49ec19d1 100644 --- a/external/tinyprocesslib/process.h +++ b/external/tinyprocesslib/tinyprocess.h @@ -1,10 +1,10 @@ #ifndef TINY_PROCESS_LIBRARY_HPP_ #define TINY_PROCESS_LIBRARY_HPP_ +#include #include #include #include -#include #include #include #ifndef _WIN32 diff --git a/flax.vcxproj b/flax.vcxproj index 3c2cf07b..0d96dac3 100644 --- a/flax.vcxproj +++ b/flax.vcxproj @@ -18,25 +18,25 @@ 15.0 {9FD6A820-341E-44AB-BA7F-A38E05B8E4F1} Win32Proj - 10.0.16299.0 + 10.0 Application true - v141 + v142 flaxc Application false - v141 + v142 flaxc Application false - v141 + v142 flaxc @@ -54,13 +54,13 @@ - D:\Projects\lib\mpir\DebugNoSyms\include;D:\Projects\lib\mpfr\DebugNoSyms\include;D:\Projects\lib\llvm\DebugNoSyms\include + D:\Projects\lib\mpir\Debug\include;D:\Projects\lib\mpfr\Debug\include;D:\Projects\lib\llvm\7.0.1\Debug\include;D:\Projects\lib\libffi\Debug\include - D:\Projects\lib\mpir\DebugNoSyms\lib;D:\Projects\lib\mpfr\DebugNoSyms\lib;D:\Projects\lib\llvm\DebugNoSyms\lib + D:\Projects\lib\mpir\Debug\lib;D:\Projects\lib\mpfr\Debug\lib;D:\Projects\lib\llvm\7.0.1\Debug\lib;D:\Projects\lib\libffi\Debug\lib - D:\Projects\lib\mpir\Release\include;D:\Projects\lib\mpfr\Release\include;D:\Projects\lib\llvm\Release\include + D:\Projects\lib\mpir\Release\include;D:\Projects\lib\mpfr\Release\include;D:\Projects\lib\llvm\7.0.1\Release\include;D:\Projects\lib\libffi\Release\include - D:\Projects\lib\mpir\Release\lib;D:\Projects\lib\mpfr\Release\lib;D:\Projects\lib\llvm\Release\lib + D:\Projects\lib\mpir\Release\lib;D:\Projects\lib\mpfr\Release\lib;D:\Projects\lib\llvm\7.0.1\Release\lib;D:\Projects\lib\libffi\Release\lib @@ -77,7 +77,7 @@ $(SolutionDir)source\include;$(SolutionDir)external;$(IncludePath) - true + false $(SolutionDir)build\sysroot\windows\$(Configuration)\ false .\build\sysroot\windows\obj\$(IntDir) @@ -100,17 +100,18 @@ ProgramDatabase Disabled Disabled - /we4062 /w14062 %(AdditionalOptions) + /utf-8 /we4062 /w14062 %(AdditionalOptions) $(DEPS_DBG_LIBS_DIR);%(AdditionalLibraryDirectories) - /ignore:4099 + /ignore:4099 /nodefaultlib:libcmt - LLVMCore.lib;LLVMSupport.lib;LLVMTarget.lib;LLVMPasses.lib;LLVMAnalysis.lib;LLVMGlobalISel.lib;LLVMLibDriver.lib;LLVMLinker.lib;LLVMipo.lib;LLVMBinaryFormat.lib;LLVMMC.lib;LLVMMCJIT.lib;LLVMOrcJIT.lib;LLVMMCParser.lib;LLVMMCDisassembler.lib;LLVMObject.lib;LLVMScalarOpts.lib;LLVMVectorize.lib;LLVMCodegen.lib;LLVMTablegen.lib;LLVMBitReader.lib;LLVMBitWriter.lib;LLVMInstrumentation.lib;LLVMRuntimeDyld.lib;LLVMInstCombine.lib;LLVMInterpreter.lib;LLVMExecutionEngine.lib;LLVMSelectionDAG.lib;LLVMTransformUtils.lib;LLVMDebugInfoCodeView.lib;LLVMDebugInfoDWARF.lib;LLVMDebugInfoMSF.lib;LLVMDebugInfoPDB.lib;LLVMAsmPrinter.lib;LLVMX86AsmPrinter.lib;LLVMProfileData.lib;LLVMX86AsmParser.lib;LLVMX86Info.lib;LLVMX86CodeGen.lib;LLVMX86Utils.lib;LLVMX86Desc.lib;LLVMDlltoolDriver.lib;mpfr.lib;mpir.lib;%(AdditionalDependencies) - LIBCMTD;%(IgnoreSpecificDefaultLibraries) + LLVMCore.lib;LLVMSupport.lib;LLVMTarget.lib;LLVMPasses.lib;LLVMAnalysis.lib;LLVMGlobalISel.lib;LLVMLibDriver.lib;LLVMLinker.lib;LLVMipo.lib;LLVMBinaryFormat.lib;LLVMMC.lib;LLVMMCJIT.lib;LLVMOrcJIT.lib;LLVMMCParser.lib;LLVMMCDisassembler.lib;LLVMObject.lib;LLVMScalarOpts.lib;LLVMVectorize.lib;LLVMCodegen.lib;LLVMTablegen.lib;LLVMBitReader.lib;LLVMBitWriter.lib;LLVMInstrumentation.lib;LLVMRuntimeDyld.lib;LLVMInstCombine.lib;LLVMInterpreter.lib;LLVMExecutionEngine.lib;LLVMSelectionDAG.lib;LLVMTransformUtils.lib;LLVMDebugInfoCodeView.lib;LLVMDebugInfoDWARF.lib;LLVMDebugInfoMSF.lib;LLVMDebugInfoPDB.lib;LLVMAsmPrinter.lib;LLVMX86AsmPrinter.lib;LLVMProfileData.lib;LLVMX86AsmParser.lib;LLVMX86Info.lib;LLVMX86CodeGen.lib;LLVMX86Utils.lib;LLVMX86Desc.lib;LLVMDlltoolDriver.lib;mpfr.lib;mpir.lib;libffi.lib + LIBCMTD;LIBCMT;%(IgnoreSpecificDefaultLibraries) true Default + true @@ -143,15 +144,15 @@ rem exit 0 true false MaxSpeed - /we4062 /w14062 %(AdditionalOptions) + /utf-8 /we4062 /w14062 %(AdditionalOptions) $(DEPS_REL_LIBS_DIR);%(AdditionalLibraryDirectories) - /ignore:4099 + /ignore:4099 /nodefaultlib:libcmt - LLVMCore.lib;LLVMSupport.lib;LLVMTarget.lib;LLVMPasses.lib;LLVMAnalysis.lib;LLVMGlobalISel.lib;LLVMLibDriver.lib;LLVMLinker.lib;LLVMipo.lib;LLVMBinaryFormat.lib;LLVMMC.lib;LLVMMCJIT.lib;LLVMOrcJIT.lib;LLVMMCParser.lib;LLVMMCDisassembler.lib;LLVMObject.lib;LLVMScalarOpts.lib;LLVMVectorize.lib;LLVMCodegen.lib;LLVMTablegen.lib;LLVMBitReader.lib;LLVMBitWriter.lib;LLVMInstrumentation.lib;LLVMRuntimeDyld.lib;LLVMInstCombine.lib;LLVMInterpreter.lib;LLVMExecutionEngine.lib;LLVMSelectionDAG.lib;LLVMTransformUtils.lib;LLVMDebugInfoCodeView.lib;LLVMDebugInfoDWARF.lib;LLVMDebugInfoMSF.lib;LLVMDebugInfoPDB.lib;LLVMAsmPrinter.lib;LLVMX86AsmPrinter.lib;LLVMProfileData.lib;LLVMX86AsmParser.lib;LLVMX86Info.lib;LLVMX86CodeGen.lib;LLVMX86Utils.lib;LLVMX86Desc.lib;LLVMDlltoolDriver.lib;mpfr.lib;mpir.lib;%(AdditionalDependencies) - LIBCMTD;%(IgnoreSpecificDefaultLibraries) + LLVMCore.lib;LLVMSupport.lib;LLVMTarget.lib;LLVMPasses.lib;LLVMAnalysis.lib;LLVMGlobalISel.lib;LLVMLibDriver.lib;LLVMLinker.lib;LLVMipo.lib;LLVMBinaryFormat.lib;LLVMMC.lib;LLVMMCJIT.lib;LLVMOrcJIT.lib;LLVMMCParser.lib;LLVMMCDisassembler.lib;LLVMObject.lib;LLVMScalarOpts.lib;LLVMVectorize.lib;LLVMCodegen.lib;LLVMTablegen.lib;LLVMBitReader.lib;LLVMBitWriter.lib;LLVMInstrumentation.lib;LLVMRuntimeDyld.lib;LLVMInstCombine.lib;LLVMInterpreter.lib;LLVMExecutionEngine.lib;LLVMSelectionDAG.lib;LLVMTransformUtils.lib;LLVMDebugInfoCodeView.lib;LLVMDebugInfoDWARF.lib;LLVMDebugInfoMSF.lib;LLVMDebugInfoPDB.lib;LLVMAsmPrinter.lib;LLVMX86AsmPrinter.lib;LLVMProfileData.lib;LLVMX86AsmParser.lib;LLVMX86Info.lib;LLVMX86CodeGen.lib;LLVMX86Utils.lib;LLVMX86Desc.lib;LLVMDlltoolDriver.lib;mpfr.lib;mpir.lib;libffi.lib + LIBCMTD;LIBCMT;%(IgnoreSpecificDefaultLibraries) false Default @@ -183,18 +184,18 @@ rem exit 0 CompileAsCpp NDEBUG;%(PreprocessorDefinitions) MultiThreadedDLL - false + true true MaxSpeed - /we4062 /w14062 %(AdditionalOptions) + /utf-8 /we4062 /w14062 %(AdditionalOptions) $(DEPS_REL_LIBS_DIR);%(AdditionalLibraryDirectories) - /ignore:4099 + /ignore:4099 /nodefaultlib:libcmt - LLVMCore.lib;LLVMSupport.lib;LLVMTarget.lib;LLVMPasses.lib;LLVMAnalysis.lib;LLVMGlobalISel.lib;LLVMLibDriver.lib;LLVMLinker.lib;LLVMipo.lib;LLVMBinaryFormat.lib;LLVMMC.lib;LLVMMCJIT.lib;LLVMOrcJIT.lib;LLVMMCParser.lib;LLVMMCDisassembler.lib;LLVMObject.lib;LLVMScalarOpts.lib;LLVMVectorize.lib;LLVMCodegen.lib;LLVMTablegen.lib;LLVMBitReader.lib;LLVMBitWriter.lib;LLVMInstrumentation.lib;LLVMRuntimeDyld.lib;LLVMInstCombine.lib;LLVMInterpreter.lib;LLVMExecutionEngine.lib;LLVMSelectionDAG.lib;LLVMTransformUtils.lib;LLVMDebugInfoCodeView.lib;LLVMDebugInfoDWARF.lib;LLVMDebugInfoMSF.lib;LLVMDebugInfoPDB.lib;LLVMAsmPrinter.lib;LLVMX86AsmPrinter.lib;LLVMProfileData.lib;LLVMX86AsmParser.lib;LLVMX86Info.lib;LLVMX86CodeGen.lib;LLVMX86Utils.lib;LLVMX86Desc.lib;LLVMDlltoolDriver.lib;mpfr.lib;mpir.lib;%(AdditionalDependencies) - LIBCMTD;%(IgnoreSpecificDefaultLibraries) + LLVMCore.lib;LLVMSupport.lib;LLVMTarget.lib;LLVMPasses.lib;LLVMAnalysis.lib;LLVMGlobalISel.lib;LLVMLibDriver.lib;LLVMLinker.lib;LLVMipo.lib;LLVMBinaryFormat.lib;LLVMMC.lib;LLVMMCJIT.lib;LLVMOrcJIT.lib;LLVMMCParser.lib;LLVMMCDisassembler.lib;LLVMObject.lib;LLVMScalarOpts.lib;LLVMVectorize.lib;LLVMCodegen.lib;LLVMTablegen.lib;LLVMBitReader.lib;LLVMBitWriter.lib;LLVMInstrumentation.lib;LLVMRuntimeDyld.lib;LLVMInstCombine.lib;LLVMInterpreter.lib;LLVMExecutionEngine.lib;LLVMSelectionDAG.lib;LLVMTransformUtils.lib;LLVMDebugInfoCodeView.lib;LLVMDebugInfoDWARF.lib;LLVMDebugInfoMSF.lib;LLVMDebugInfoPDB.lib;LLVMAsmPrinter.lib;LLVMX86AsmPrinter.lib;LLVMProfileData.lib;LLVMX86AsmParser.lib;LLVMX86Info.lib;LLVMX86CodeGen.lib;LLVMX86Utils.lib;LLVMX86Desc.lib;LLVMDlltoolDriver.lib;mpfr.lib;mpir.lib;libffi.lib + LIBCMTD;LIBCMT;%(IgnoreSpecificDefaultLibraries) false Default @@ -236,7 +237,7 @@ rem exit 0 - + @@ -271,4 +272,4 @@ rem exit 0 - + \ No newline at end of file diff --git a/flax.vcxproj.user b/flax.vcxproj.user deleted file mode 100644 index 87d7f4a4..00000000 --- a/flax.vcxproj.user +++ /dev/null @@ -1,19 +0,0 @@ - - - - -sysroot $(ProjectDir)build\sysroot -run build\supertiny.flx - $(ProjectDir) - WindowsLocalDebugger - - - true - - - -sysroot $(ProjectDir)build\sysroot -run build\tester.flx - WindowsLocalDebugger - - - -sysroot $(ProjectDir)build\sysroot -run -O0 build\massive.flx - WindowsLocalDebugger - - \ No newline at end of file diff --git a/issues.md b/issues.md index 2a128bba..8f07aae1 100644 --- a/issues.md +++ b/issues.md @@ -3,7 +3,7 @@ Note: this is just a personal log of outstanding issues, shorter rants/ramblings, and a changelog that doesn't need me to scroll through git. -### FEATURES TO IMPLEMENT +## FEATURES TO IMPLEMENT 1. Number literals still suck @@ -42,49 +42,21 @@ Note: this is just a personal log of outstanding issues, shorter rants/ramblings ----- -### THINGS TO FIX +## THINGS TO FIX 2. There are still some instances where we explicitly 'initialise' a class equivalent to `memset(0)` -- see *THINGS TO NOTE* below. ------ - - -### POLYMORPHIC PIPELINE DOCUMENTATION - -So, this thing serves as the shitty documentation for how the generic pipeline works for future implementations. - -1. When AST nodes are typechecked at the top level, if they are polymorphic things they will refuse to be checked, - and won't return a result (`ast::Block` does the same check), but instead add themselves to a list of pending, - uninstantiated generic types in the `sst::StateTree`. - -2. When resolving a reference (function call or identifier), we check the pending generic list if we can't find any - normal things that match (or if we already have some explicit mappings). If there is a match, we call into - `attemptToDisambiguateGenericReference()` with whatever information we have (eg. partial solutions) - -3. We call `inferTypesForGenericEntity` which is the main solver function that infers types for the type arguments - that are missing. This just acts like a black box for the most part. - -4. If we find that we're actually a reference to a function (ie. we act like a function pointer instead of a call) then - we do a thing called `fillGenericTypeWithPlaceholders` which puts in fake 'solutions' for each type argument (aka - `fir::PolyPlaceholderType`), and proceeds to return it. - -5. We will then allow that to typecheck. For now, only functions can have placeholder types, so we special-case that in - function typechecking by just skipping the body typecheck when we have placeholders. - -6. Once we manage to solve everything -- ie. get rid of all the placeholder types, we need to re-typecheck the original definition - with proper filled in types. This happens in `resolveFunctionCall`. - - - ------ -### THINGS TO INVESTIGATE +## THINGS TO INVESTIGATE + +* ### Possibly allow struct-constructors to init transparent fields -0. Errors need to propagate better +* ### Errors need to propagate better Right now with the newly-implemented `PrettyError` system, we can propagate a lot more information upwards, and with the new thing of throwing an error when we unwrap a `TCResult`, there's less need to be explicit when handling errors during typechecking. @@ -111,7 +83,7 @@ So, this thing serves as the shitty documentation for how the generic pipeline w -1. Certain cases we still allow a zeroinitialised class to exist: +* ### Certain cases we still allow a zeroinitialised class to exist: A. When growing a dynamic array, the new elements are zeroinitialised instead of having a value set (for non-class types, they get their default value) B. When initialising a field in a struct, if not explicitly assigned to it is left as it is -- which should be the zeroinitialiser C. When initialising a field in a class without an inline initialiser, we explicitly set it to 0. @@ -126,11 +98,11 @@ So, this thing serves as the shitty documentation for how the generic pipeline w `var foo: SomeStruct` without an initialiser... -3. Foreach loops where you take more than one thing at a time, like this, maybe: +* ### Foreach loops where you take more than one thing at a time, like this, maybe: `for [ first, middle, last, ... ] in list { ... }` -4. Some kind of construct to make a variable immutable beyond a certain point; thus you could have arbitrarily complex initialisation code, +* ### Some kind of construct to make a variable immutable beyond a certain point; thus you could have arbitrarily complex initialisation code, then have the compiler enforce that your variable is immutable after that point, eg: ``` @@ -146,7 +118,7 @@ So, this thing serves as the shitty documentation for how the generic pipeline w ``` -5. Type inference for single-expr functions? It's a little weird to have two arrows like this: +* ### Type inference for single-expr functions? It's a little weird to have two arrows like this: `fn foo(a: T) -> T => a * a` The type inference would require some re-working though, because to generate the declaration of the function we need the return type, but to @@ -159,7 +131,8 @@ So, this thing serves as the shitty documentation for how the generic pipeline w It's not really a high-priority thing anyway. -6. wrt. optional arguments, you *must* refer to it by name to specify a value. For example: +* ### wrt. optional arguments + you *must* refer to it by name to specify a value. For example: `fn foo(a: int, b: int = 3) => ...` @@ -177,21 +150,21 @@ So, this thing serves as the shitty documentation for how the generic pipeline w ``` -8. https://proandroiddev.com/understanding-generics-and-variance-in-kotlin-714c14564c47 +* https://proandroiddev.com/understanding-generics-and-variance-in-kotlin-714c14564c47 https://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science) -10. Arguments-after-varargs (see https://youtu.be/mGe5d6dPHAU?t=1379) +* Arguments-after-varargs (see https://youtu.be/mGe5d6dPHAU?t=1379) Basically, allow passing named parameters after var-args. -11. Optimisation: use interned strings for comparison (and for use as keys in all the hashmaps we have), hopefully allowing a large-ish speedup since +* Optimisation: use interned strings for comparison (and for use as keys in all the hashmaps we have), hopefully allowing a large-ish speedup since (according to historical profiles) `std::string`-related things are the cause of a lot of slowdowns. -12. Right now we design the `any` type to not handle refcounted types at all -- meaning it doesn't touch the refcount of those, and thus +* Right now we design the `any` type to not handle refcounted types at all -- meaning it doesn't touch the refcount of those, and thus the `any` can outlive the referred-to value. To change this, `any` itself would need to be refcounted, but then we'd need to be able to check whether something was refcounted @@ -201,7 +174,7 @@ So, this thing serves as the shitty documentation for how the generic pipeline w -13. A similar issue with refcounting for casting, though i'm not entirely sure if this is a real issue or just something that i'm imagining. +* A similar issue with refcounting for casting, though i'm not entirely sure if this is a real issue or just something that i'm imagining. So, when we assign something `x = y`, then it is necessary that the types of `x` and `y` are the same. Then, if the type in question is a reference counted type, we do some `autoAssignRefCountedValue()`, that does separate things for lvalues and rvalues. If the right-hand side @@ -239,6 +212,33 @@ So, this thing serves as the shitty documentation for how the generic pipeline w +## POLYMORPHIC PIPELINE DOCUMENTATION + +So, this thing serves as the shitty documentation for how the generic pipeline works for future implementations. + +1. When AST nodes are typechecked at the top level, if they are polymorphic things they will refuse to be checked, + and won't return a result (`ast::Block` does the same check), but instead add themselves to a list of pending, + uninstantiated generic types in the `sst::StateTree`. + +2. When resolving a reference (function call or identifier), we check the pending generic list if we can't find any + normal things that match (or if we already have some explicit mappings). If there is a match, we call into + `attemptToDisambiguateGenericReference()` with whatever information we have (eg. partial solutions) + +3. We call `inferTypesForGenericEntity` which is the main solver function that infers types for the type arguments + that are missing. This just acts like a black box for the most part. + +4. If we find that we're actually a reference to a function (ie. we act like a function pointer instead of a call) then + we do a thing called `fillGenericTypeWithPlaceholders` which puts in fake 'solutions' for each type argument (aka + `fir::PolyPlaceholderType`), and proceeds to return it. + +5. We will then allow that to typecheck. For now, only functions can have placeholder types, so we special-case that in + function typechecking by just skipping the body typecheck when we have placeholders. + +6. Once we manage to solve everything -- ie. get rid of all the placeholder types, we need to re-typecheck the original definition + with proper filled in types. This happens in `resolveFunctionCall`. + + + diff --git a/libs/OpenGL/GL.flx b/libs/OpenGL/GL.flx index 016f1afd..577fe820 100644 --- a/libs/OpenGL/GL.flx +++ b/libs/OpenGL/GL.flx @@ -1,5 +1,5 @@ // GL.flx -// Copyright (c) 2014 - 2016, zhiayang@gmail.com +// Copyright (c) 2014 - 2016, zhiayang // Licensed under the Apache License Version 2.0. public enum GL: u32 diff --git a/libs/OpenGL/GLUT.flx b/libs/OpenGL/GLUT.flx index be0b6dbd..6c676081 100644 --- a/libs/OpenGL/GLUT.flx +++ b/libs/OpenGL/GLUT.flx @@ -1,5 +1,5 @@ // GLUT.flx -// Copyright (c) 2014 - 2016, zhiayang@gmail.com +// Copyright (c) 2014 - 2016, zhiayang // Licensed under the Apache License Version 2.0. public namespace GLUT diff --git a/libs/SDL2/Keyboard.flx b/libs/SDL2/Keyboard.flx index e0399648..e0b6ba05 100644 --- a/libs/SDL2/Keyboard.flx +++ b/libs/SDL2/Keyboard.flx @@ -1,5 +1,5 @@ // Keyboard.flx -// Copyright (c) 2014 - 2016, zhiayang@gmail.com +// Copyright (c) 2014 - 2016, zhiayang // Licensed under the Apache License Version 2.0. public namespace SDL diff --git a/libs/SDL2/SDL.flx b/libs/SDL2/SDL.flx index 6295c3be..ec9f193a 100644 --- a/libs/SDL2/SDL.flx +++ b/libs/SDL2/SDL.flx @@ -1,5 +1,5 @@ // SDL.flx -// Copyright (c) 2014 - 2016, zhiayang@gmail.com +// Copyright (c) 2014 - 2016, zhiayang // Licensed under the Apache License Version 2.0. import "Keyboard" diff --git a/libs/libc.flx b/libs/libc.flx index 47a146f9..edf596d1 100644 --- a/libs/libc.flx +++ b/libs/libc.flx @@ -1,5 +1,5 @@ // libc.flx -// Copyright (c) 2014 - 2015, zhiayang@gmail.com +// Copyright (c) 2014 - 2015, zhiayang // Licensed under the Apache License Version 2.0. export libc @@ -12,6 +12,9 @@ public ffi fn snprintf(x: &i8, l: u64, y: &i8, ...) -> i32 public ffi fn fprintf(stream: &void, y: &i8, ...) -> i32 +public ffi fn putchar(x: i32) -> i32 + + // memcpy/set/move public ffi fn memcpy(dest: &i8, source: &i8, length: u64) -> &i8 public ffi fn memmove(dest: &i8, source: &i8, length: u64) -> &i8 @@ -37,9 +40,40 @@ public ffi fn fsync(fd: i32) -> i32 public ffi fn fflush(fd: &void) -> i32 public ffi fn ioctl(fd: i32, cmd: u64, ...) -> i32 -public ffi fn fdopen(fd: i32, mode: &i8) -> &void as "_fdopen" - - public ffi fn fopen(path: &i8, mode: &i8) -> &void public ffi fn fread(buf: &i8, sz: u64, cnt: u64, file: &void) -> u64 -public ffi fn fclose(file: &void) -> i32 \ No newline at end of file +public ffi fn fclose(file: &void) -> i32 + +// unistd.h + + +// posix stuff, that windows likes to rename for some reason!!!!! +#if os::name == "windows" +{ + // unistd.h + public ffi fn read(fd: i32, buf: &i8, count: i64) -> i64 as "_read" + public ffi fn write(fd: i32, buf: &i8, count: i64) -> i64 as "_write" + + // stdio.h + public ffi fn fdopen(fd: i32, mode: &i8) -> &void as "_fdopen" +} +else +{ + // unistd.h + public ffi fn read(fd: i32, buf: &i8, count: i64) -> i64 + public ffi fn write(fd: i32, buf: &i8, count: i64) -> i64 + + // stdio.h + public ffi fn fdopen(fd: i32, mode: &i8) -> &void +} + + + + + + + + + + + diff --git a/libs/os.flx b/libs/os.flx new file mode 100644 index 00000000..f426e3e4 --- /dev/null +++ b/libs/os.flx @@ -0,0 +1,5 @@ +// os.flx +// Copyright (c) 2019, zhiayang +// Licensed under the Apache License Version 2.0. + +export os diff --git a/libs/std/io.flx b/libs/std/io.flx index 36ca4942..1f1a8bbd 100644 --- a/libs/std/io.flx +++ b/libs/std/io.flx @@ -1,5 +1,5 @@ // stdio.flx -// Copyright (c) 2017, zhiayang@gmail.com +// Copyright (c) 2017, zhiayang // Licensed under the Apache License Version 2.0. export std::io @@ -8,13 +8,9 @@ import libc let ASCII_BACKSLASH: i8 = 92 let ASCII_PERCENT: i8 = 37 -// this exists solely for semantic reasons. -fn char(c: i8) -> i8 => c -fn char(c: str) -> i8 => c[0] - fn error(msg: str) { - libc::printf("Invalid format specifier: %s\n", msg) + libc::printf("invalid format string: '%s'\n", msg) libc::abort() } @@ -66,7 +62,7 @@ public fn format(fmt: str, args: [any: ...]) -> string while idx < fmt.length { let ch = fmt[idx] - if ch == char("%") + if ch == '%' { if argi >= args.length { @@ -98,7 +94,7 @@ public fn format(fmt: str, args: [any: ...]) -> string } else { - if ch == char("\\") + if ch == '\\' { idx += 1 ret.append(fmt[idx]) @@ -118,10 +114,14 @@ public fn format(fmt: str, args: [any: ...]) -> string public fn println(fmt: str, args: [any: ...]) { - libc::printf("%s\n", format(fmt, ...args).ptr) + libc::puts(format(fmt, ...args)) } - +public fn print(fmt: str, args: [any: ...]) +{ + let s = format(fmt, ...args) + libc::write(1, s.ptr, s.length) +} diff --git a/libs/std/limits.flx b/libs/std/limits.flx index 062bbfae..4d0d5b2e 100644 --- a/libs/std/limits.flx +++ b/libs/std/limits.flx @@ -1,5 +1,5 @@ // limits.flx -// Copyright (c) 2014 - 2016, zhiayang@gmail.com +// Copyright (c) 2014 - 2016, zhiayang // Licensed under the Apache License Version 2.0. export std::limits diff --git a/libs/std/map.flx b/libs/std/map.flx index 4b736fdf..b62d0d82 100644 --- a/libs/std/map.flx +++ b/libs/std/map.flx @@ -1,5 +1,5 @@ // map.flx -// Copyright (c) 2017, zhiayang@gmail.com +// Copyright (c) 2017, zhiayang // Licensed under the Apache License Version 2.0. export std diff --git a/libs/std/math.flx b/libs/std/math.flx index 08450886..bc897e29 100644 --- a/libs/std/math.flx +++ b/libs/std/math.flx @@ -1,5 +1,5 @@ // math.flx -// Copyright (c) 2014 - 2015, zhiayang@gmail.com +// Copyright (c) 2014 - 2015, zhiayang // Licensed under the Apache License Version 2.0. export math @@ -18,4 +18,4 @@ public ffi fn powf(x: f32, y: f32) -> f32 public let π: f64 = 3.14159265358979323846264338327950 -public let pi: f64 = 3.14159265358979323846264338327950 \ No newline at end of file +public let pi: f64 = 3.14159265358979323846264338327950 diff --git a/libs/std/opt.flx b/libs/std/opt.flx index 99382f1c..4629c74e 100644 --- a/libs/std/opt.flx +++ b/libs/std/opt.flx @@ -1,5 +1,5 @@ // opt.flx -// Copyright (c) 2017, zhiayang@gmail.com +// Copyright (c) 2017, zhiayang // Licensed under the Apache License Version 2.0. export std @@ -8,4 +8,4 @@ public union opt { some: T none -} \ No newline at end of file +} diff --git a/libs/std/set.flx b/libs/std/set.flx index 06fe4ad0..6dbc2c2f 100644 --- a/libs/std/set.flx +++ b/libs/std/set.flx @@ -1,5 +1,5 @@ // set.flx -// Copyright (c) 2017, zhiayang@gmail.com +// Copyright (c) 2017, zhiayang // Licensed under the Apache License Version 2.0. export std::set @@ -16,4 +16,4 @@ public class set } var root: &node -} \ No newline at end of file +} diff --git a/makefile b/makefile index 1db9db64..c2046bf6 100644 --- a/makefile +++ b/makefile @@ -41,7 +41,7 @@ NUMFILES := $$(($(words $(CXXSRC)) + $(words $(CSRC)))) DEFINES := -D__USE_MINGW_ANSI_STDIO=1 SANITISE := -CXXFLAGS += -std=c++1z -O0 -g -c -Wall -frtti -fexceptions -fno-omit-frame-pointer -Wno-old-style-cast $(SANITISE) $(DEFINES) +CXXFLAGS += -std=c++17 -O0 -g -c -Wall -frtti -fexceptions -fno-omit-frame-pointer -Wno-old-style-cast $(SANITISE) $(DEFINES) CFLAGS += -std=c11 -O0 -g -c -Wall -fno-omit-frame-pointer -Wno-overlength-strings $(SANITISE) $(DEFINES) LDFLAGS += $(SANITISE) @@ -57,32 +57,49 @@ SUPERTINYSRC := build/supertiny.flx GLTESTSRC := build/gltest.flx TESTSRC := build/tester.flx +UNAME_IDENT := $(shell uname) +COMPILER_IDENT := $(shell $(CC) --version | head -n 1) -.DEFAULT_GOAL = osx --include $(CXXDEPS) +ifeq ("$(UNAME_IDENT)","Darwin") + LIBFFI_CFLAGS := $(shell env PKG_CONFIG_PATH=/usr/local/opt/libffi/lib/pkgconfig pkg-config --cflags libffi) + LIBFFI_LDFLAGS := $(shell env PKG_CONFIG_PATH=/usr/local/opt/libffi/lib/pkgconfig pkg-config --libs libffi) +else + LIBFFI_CFLAGS := $(shell pkg-config --cflags libffi) + LIBFFI_LDFLAGS := $(shell pkg-config --libs libffi) + + # on linux, we need to explicitly export our functions + # like __declspec(dllexport) except __attribute__((visibility("default"))) + LDFLAGS += -Wl,--export-dynamic +endif +MPFR_CFLAGS := $(shell pkg-config --cflags mpfr) +MPFR_LDFLAGS := $(shell pkg-config --libs mpfr) -.PHONY: copylibs jit compile clean build osx linux ci satest tiny osxflags +CFLAGS += $(MPFR_CFLAGS) $(LIBFFI_CFLAGS) +CXXFLAGS += $(MPFR_CFLAGS) $(LIBFFI_CFLAGS) +LDFLAGS += $(MPFR_LDFLAGS) $(LIBFFI_LDFLAGS) -osxflags: CXXFLAGS += -march=native -fmodules -Weverything -Xclang -fcolor-diagnostics $(SANITISE) $(CLANGWARNINGS) -osxflags: CFLAGS += -fmodules -Xclang -fcolor-diagnostics $(SANITISE) $(CLANGWARNINGS) +ifneq (,$(findstring clang,$(COMPILER_IDENT))) + CXXFLAGS += -Wall -Xclang -fcolor-diagnostics $(SANITISE) $(CLANGWARNINGS) + CFLAGS += -Xclang -fcolor-diagnostics $(SANITISE) $(CLANGWARNINGS) +endif -osxflags: +.DEFAULT_GOAL = jit +-include $(CXXDEPS) -osx: jit osxflags -satest: osxflags build +.PHONY: copylibs jit compile clean build linux ci satest tiny + +satest: build @$(OUTPUT) $(FLXFLAGS) -run build/standalone.flx -tester: osxflags build +tester: build @$(OUTPUT) $(FLXFLAGS) -run build/tester.flx ci: test -linux: jit - jit: build @$(OUTPUT) $(FLXFLAGS) -run -o $(SUPERTINYBIN) $(SUPERTINYSRC) @@ -115,7 +132,7 @@ copylibs: $(FLXSRC) $(OUTPUT): $(PRECOMP_GCH) $(CXXOBJ) $(COBJ) @printf "# linking\n" @mkdir -p $(dir $(OUTPUT)) - @$(CXX) -o $@ $(CXXOBJ) $(COBJ) $(shell $(LLVM_CONFIG) --cxxflags --ldflags --system-libs --libs core engine native linker bitwriter lto vectorize all-targets object orcjit) -lmpfr -lgmp $(LDFLAGS) -lpthread + @$(CXX) -o $@ $(CXXOBJ) $(COBJ) $(shell $(LLVM_CONFIG) --cxxflags --ldflags --system-libs --libs core engine native linker bitwriter lto vectorize all-targets object orcjit) -lmpfr -lgmp $(LDFLAGS) -lpthread -ldl -lffi %.cpp.o: %.cpp diff --git a/meson.build b/meson.build index 36c7e4ea..b84a56d0 100644 --- a/meson.build +++ b/meson.build @@ -1,44 +1,151 @@ -project('flax', ['c', 'cpp']) +project('flax', version: '0.40.0-pre') -the_compiler = meson.get_compiler('c') - -# buildKind = get_option('buildtype') -# message('build type is ' + buildKind) - -if get_option('buildtype') == 'debug' - libKind = 'Debug' -else - libKind = 'Release' -endif +add_languages(['c', 'cpp']) -mpir_root_dir = 'D:/Projects/lib/mpir' -mpfr_root_dir = 'D:/Projects/lib/mpfr' -llvm_root_dir = 'D:/Projects/lib/llvm/7.0.1/' -mpir_hdr_dir = mpir_root_dir + '/' + libKind + '/include/' -mpfr_hdr_dir = mpfr_root_dir + '/' + libKind + '/include/' -llvm_hdr_dir = llvm_root_dir + '/' + libKind + '/include/' +the_compiler = meson.get_compiler('c') -mpir_lib_dir = mpir_root_dir + '/' + libKind + '/lib/' -mpfr_lib_dir = mpfr_root_dir + '/' + libKind + '/lib/' -llvm_lib_dir = llvm_root_dir + '/' + libKind + '/lib/' if the_compiler.get_id() == 'msvc' + add_project_arguments('/utf-8', language: 'cpp') add_project_arguments('/std:c++17', language: 'cpp') add_project_arguments('/permissive-', language: 'cpp') + add_project_arguments('-D_STDC_LIMIT_MACROS', language: 'cpp') add_project_arguments('-D_SCL_SECURE_NO_WARNINGS', language: 'cpp') add_project_arguments('-D_SILENCE_CXX17_ITERATOR_BASE_CLASS_DEPRECATION_WARNING', language: 'cpp') + add_project_link_arguments('/ignore:4099', language: ['c', 'cpp']) add_project_link_arguments('/machine:X64', language: 'cpp') add_project_link_arguments('/incremental', language: 'cpp') add_project_link_arguments('/debug:fastlink', language: ['c', 'cpp']) add_project_link_arguments('/nodefaultlib:libcmt.lib', language: 'cpp') add_project_link_arguments('/nodefaultlib:libcmtd.lib', language: 'cpp') -else - # add_project_arguments('--std=c++17', language: 'cpp') - # add_project_arguments('--std=c11', language: 'c') - error('You fool, use the makefile for non-windows builds') + if get_option('buildtype') == 'debug' + libKind = 'Debug' + else + libKind = 'Release' + endif + + # ok, so because meson is dumb, and we want to allow configuring the locations *without* editing this file + # eg. in CI environments, we run an external command that's just an echo of the environment variable, and + # capture the output to use. all because it's apparently "a bad idea" to allow people to read env vars... + + envname_mpir = '%MPIR_ROOT_DIR%' + envname_mpfr = '%MPFR_ROOT_DIR%' + envname_llvm = '%LLVM_ROOT_DIR%' + envname_libffi = '%LIBFFI_ROOT_DIR%' + + llvm_version = '7.1.0' + + mpir_root_dir = run_command('cmd.exe', '/C', 'echo', envname_mpir).stdout().strip() + if mpir_root_dir == envname_mpir + mpir_root_dir = 'D:/Projects/lib/mpir' + endif + + mpfr_root_dir = run_command('cmd.exe', '/C', 'echo', envname_mpfr).stdout().strip() + if mpfr_root_dir == envname_mpfr + mpfr_root_dir = 'D:/Projects/lib/mpfr' + endif + + llvm_root_dir = run_command('cmd.exe', '/C', 'echo', envname_llvm).stdout().strip() + if llvm_root_dir == envname_llvm + llvm_root_dir = 'D:/Projects/lib/llvm/' + llvm_version + endif + + libffi_root_dir = run_command('cmd.exe', '/C', 'echo', envname_libffi).stdout().strip() + if libffi_root_dir == envname_libffi + libffi_root_dir = 'D:/Projects/lib/libffi' + endif + + message('mpir_root: ' + mpir_root_dir) + message('mpfr_root: ' + mpfr_root_dir) + message('llvm_root: ' + llvm_root_dir) + message('libffi_root: ' + libffi_root_dir) + + mpir_hdr_dir = mpir_root_dir + '/' + libKind + '/include/' + mpfr_hdr_dir = mpfr_root_dir + '/' + libKind + '/include/' + llvm_hdr_dir = llvm_root_dir + '/' + libKind + '/include/' + libffi_hdr_dir = libffi_root_dir + '/' + libKind + '/include/' + + mpir_lib_dir = mpir_root_dir + '/' + libKind + '/lib/' + mpfr_lib_dir = mpfr_root_dir + '/' + libKind + '/lib/' + llvm_lib_dir = llvm_root_dir + '/' + libKind + '/lib/' + libffi_lib_dir = libffi_root_dir + '/' + libKind + '/lib/' + + legacy_stdio_dep = declare_dependency(dependencies: the_compiler.find_library('legacy_stdio_definitions')) + + mpir_dep = declare_dependency(version: '3.0.0', include_directories: include_directories(mpir_hdr_dir), + dependencies: the_compiler.find_library('mpir', dirs: mpir_lib_dir)) + + mpfr_dep = declare_dependency(version: '4.0.0', include_directories: include_directories(mpfr_hdr_dir), + dependencies: the_compiler.find_library('mpfr', dirs: mpfr_lib_dir)) + + libffi_dep = declare_dependency(version: '3.2.1', include_directories: include_directories(libffi_hdr_dir), + dependencies: the_compiler.find_library('libffi', dirs: libffi_lib_dir)) + + llvm_dep = declare_dependency(version: llvm_version, include_directories: include_directories(llvm_hdr_dir), + dependencies: [ mpfr_dep, mpir_dep, + the_compiler.find_library('LLVMMC', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMipo', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMCore', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMMCJIT', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMOrcJIT', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMLinker', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMTarget', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMObject', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMPasses', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMX86Info', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMCodegen', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMX86Desc', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMSupport', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMMCParser', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMX86Utils', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMAnalysis', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMTablegen', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMVectorize', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMBitReader', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMLibDriver', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMBitWriter', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMAsmPrinter', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMGlobalISel', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMScalarOpts', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMX86CodeGen', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMProfileData', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMInstCombine', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMRuntimeDyld', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMSelectionDAG', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMBinaryFormat', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMX86AsmParser', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMX86AsmPrinter', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMTransformUtils', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMMCDisassembler', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMExecutionEngine', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMDebugInfoCodeView', dirs: llvm_lib_dir) + ] + ) + + all_deps = [ legacy_stdio_dep, mpir_dep, mpfr_dep, libffi_dep, llvm_dep ] +else + add_project_arguments('-std=c11', language: 'c') + add_project_arguments('-std=c++17', language: 'cpp') + add_project_arguments('-include', 'source/include/precompile.h', language: 'cpp') + + add_project_arguments('-Wall', '-Wno-unused-parameter', '-Wno-sign-conversion', '-Wno-padded', '-Wno-conversion', + '-Wno-shadow', '-Wno-missing-noreturn', '-Wno-unused-macros', '-Wno-switch-enum', '-Wno-deprecated', '-Wno-format-nonliteral', + '-Wno-trigraphs', '-Wno-unused-const-variable', '-Wno-deprecated-declarations', '-Wno-unused-lambda-capture', + '-Wno-unused-variable', + language:'cpp') + + # on unix, we use GMP instead of MPIR. + mpfr_dep = dependency('mpfr', version: '>= 4.0.0') + libffi_dep = dependency('libffi', version: '>= 3.2.1') + llvm_dep = dependency('llvm', static: true, version: '7.0.1', modules: [ + 'core', 'engine', 'native', 'linker', 'bitwriter', + 'lto', 'vectorize', 'all-targets', 'object', 'orcjit' + ]) + + all_deps = [ mpfr_dep, libffi_dep, llvm_dep ] endif @@ -77,6 +184,9 @@ source_files = files([ 'source/backend/llvm/linker.cpp', 'source/backend/llvm/translator.cpp', + 'source/backend/interp/driver.cpp', + + 'source/typecheck/misc.cpp', 'source/typecheck/call.cpp', 'source/typecheck/type.cpp', 'source/typecheck/using.cpp', @@ -100,6 +210,7 @@ source_files = files([ 'source/typecheck/subscript.cpp', 'source/typecheck/operators.cpp', 'source/typecheck/arithmetic.cpp', + 'source/typecheck/directives.cpp', 'source/typecheck/destructure.cpp', 'source/typecheck/controlflow.cpp', 'source/typecheck/typecheckstate.cpp', @@ -136,6 +247,7 @@ source_files = files([ 'source/codegen/operators.cpp', 'source/codegen/subscript.cpp', 'source/codegen/arithmetic.cpp', + 'source/codegen/directives.cpp', 'source/codegen/destructure.cpp', 'source/codegen/refcounting.cpp', 'source/codegen/controlflow.cpp', @@ -149,6 +261,10 @@ source_files = files([ 'source/codegen/glue/strings.cpp', 'source/codegen/glue/saa_common.cpp', + 'source/fir/interp/wrappers.cpp', + 'source/fir/interp/compiler.cpp', + 'source/fir/interp/interpreter.cpp', + 'source/fir/ConstantValue.cpp', 'source/fir/GlobalValue.cpp', 'source/fir/Instruction.cpp', @@ -165,6 +281,7 @@ source_files = files([ 'source/fir/Types/RawUnionType.cpp', 'source/fir/Types/PointerType.cpp', 'source/fir/Types/SingleTypes.cpp', + 'source/fir/Types/OpaqueType.cpp', 'source/fir/Types/StructType.cpp', 'source/fir/Types/ArrayType.cpp', 'source/fir/Types/ClassType.cpp', @@ -189,7 +306,6 @@ utf8rewind_dep = declare_dependency(include_directories: include_directories('ex ] ) - tinyproclib_dep = declare_dependency(include_directories: include_directories('external/tinyprocesslib'), sources: [ 'external/tinyprocesslib/process.cpp', @@ -199,69 +315,15 @@ tinyproclib_dep = declare_dependency(include_directories: include_directories('e -mpir_dep = declare_dependency(version: '3.0.0', include_directories: include_directories(mpir_hdr_dir), - dependencies: the_compiler.find_library('mpir', dirs: mpir_lib_dir)) - -mpfr_dep = declare_dependency(version: '4.0.0', include_directories: include_directories(mpfr_hdr_dir), - dependencies: the_compiler.find_library('mpfr', dirs: mpfr_lib_dir)) - -llvm_dep = declare_dependency(version: '7.0.1', include_directories: include_directories(llvm_hdr_dir), - dependencies: [ mpfr_dep, mpir_dep, - # the_compiler.find_library('LLVM_all', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMMC', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMipo', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMCore', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMMCJIT', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMOrcJIT', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMLinker', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMTarget', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMObject', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMPasses', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMX86Info', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMCodegen', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMX86Desc', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMSupport', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMMCParser', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMX86Utils', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMAnalysis', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMTablegen', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMVectorize', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMBitReader', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMLibDriver', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMBitWriter', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMAsmPrinter', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMGlobalISel', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMScalarOpts', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMX86CodeGen', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMProfileData', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMInstCombine', dirs: llvm_lib_dir), - # the_compiler.find_library('LLVMInterpreter', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMRuntimeDyld', dirs: llvm_lib_dir), - # the_compiler.find_library('LLVMDebugInfoPDB', dirs: llvm_lib_dir), - # the_compiler.find_library('LLVMDebugInfoMSF', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMSelectionDAG', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMBinaryFormat', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMX86AsmParser', dirs: llvm_lib_dir), - # the_compiler.find_library('LLVMDlltoolDriver', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMX86AsmPrinter', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMTransformUtils', dirs: llvm_lib_dir), - # the_compiler.find_library('LLVMDebugInfoDWARF', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMMCDisassembler', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMExecutionEngine', dirs: llvm_lib_dir), - # the_compiler.find_library('LLVMInstrumentation', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMDebugInfoCodeView', dirs: llvm_lib_dir) - ] +executable('flaxc', source_files, + include_directories: include_directories([ 'source/include', 'external' ]), + dependencies: all_deps + [ tinyproclib_dep, utf8rewind_dep ] ) -include_dirs = include_directories([ 'source/include', 'external', mpir_hdr_dir, mpfr_hdr_dir, llvm_hdr_dir ]) -executable('flaxc', source_files, - include_directories: include_dirs, - dependencies: [ llvm_dep, tinyproclib_dep, utf8rewind_dep, mpfr_dep, mpir_dep ] -) - # cpp_pch: [ 'source/include/precompile.h', 'source/include/precompile.cpp' ] + diff --git a/programs/fic/main.flx b/programs/fic/main.flx index 5362f0ca..b808a243 100644 --- a/programs/fic/main.flx +++ b/programs/fic/main.flx @@ -1,9 +1,9 @@ // main.flx -// Copyright (c) 2017, zhiayang@gmail.com +// Copyright (c) 2017, zhiayang // Licensed under the Apache License Version 2.0. // this is supposed to be an irc client, in case i forget. @entry fn main() { -} \ No newline at end of file +} diff --git a/source/backend/backend.cpp b/source/backend/backend.cpp index 90b18dbd..8af1c853 100644 --- a/source/backend/backend.cpp +++ b/source/backend/backend.cpp @@ -1,9 +1,9 @@ // Backend.cpp -// Copyright (c) 2014 - 2016, zhiayang@gmail.com +// Copyright (c) 2014 - 2016, zhiayang // Licensed under the Apache License Version 2.0. -#include "backend.h" #include "backends/llvm.h" +#include "backends/interp.h" namespace backend { @@ -14,6 +14,9 @@ namespace backend case BackendOption::LLVM: return new LLVMBackend(cd, in, out); + case BackendOption::Interpreter: + return new FIRInterpBackend(cd, in, out); + case BackendOption::Assembly_x64: return new x64Backend(cd, in, out); @@ -22,7 +25,7 @@ namespace backend case BackendOption::Invalid: default: - _error_and_exit("Invalid backend"); + _error_and_exit("invalid backend\n"); } } diff --git a/source/backend/interp/driver.cpp b/source/backend/interp/driver.cpp new file mode 100644 index 00000000..b287c13a --- /dev/null +++ b/source/backend/interp/driver.cpp @@ -0,0 +1,107 @@ +// driver.cpp +// Copyright (c) 2019, zhiayang +// Licensed under the Apache License Version 2.0. + +#include + +#include "defs.h" +#include "backend.h" +#include "frontend.h" +#include "platform.h" + +#include "ir/interp.h" +#include "ir/module.h" + +#include "backends/interp.h" + + +namespace backend +{ + template + static void _printTiming(T ts, const std::string& thing) + { + if(frontend::getPrintProfileStats()) + { + auto dur = std::chrono::high_resolution_clock::now() - ts; + auto ms = (double) dur.count() / 1000.0 / 1000.0; + printf("%s took %.1f ms%s\n", thing.c_str(), ms, ms > 3000 ? strprintf(" (aka %.2f s)", ms / 1000.0).c_str() : ""); + } + } + + + using namespace fir; + using namespace fir::interp; + + FIRInterpBackend::FIRInterpBackend(CompiledData& dat, std::vector inputs, std::string output) + : Backend(BackendCaps::JIT, dat, inputs, output) + { + platform::performSelfDlOpen(); + } + + std::string FIRInterpBackend::str() + { + return "FIR Interpreter"; + } + + void FIRInterpBackend::performCompilation() + { + this->is = new InterpState(this->compiledData.module); + this->is->initialise(); + + // it suffices to compile just the entry function. + this->is->compileFunction(this->compiledData.module->getEntryFunction()); + } + + void FIRInterpBackend::optimiseProgram() + { + // nothing. + } + + void FIRInterpBackend::writeOutput() + { + if(auto entryfn = this->compiledData.module->getEntryFunction(); entryfn) + { + auto ts = std::chrono::high_resolution_clock::now(); + + + auto f = this->is->compiledFunctions[entryfn]; + + // add arguments if necessary, i guess. + // just make null values. + std::vector args; + for(auto a : entryfn->getArguments()) + args.push_back(this->is->makeValue(a)); + + this->is->runFunction(f, args); + + _printTiming(ts, "interp"); + } + else + { + error("interp: no entry function, cannot run!"); + } + } +} + + + + + + + + + + + + + + + + + + + + + + + diff --git a/source/backend/llvm/jit.cpp b/source/backend/llvm/jit.cpp index 9c060dbf..726ce5d1 100644 --- a/source/backend/llvm/jit.cpp +++ b/source/backend/llvm/jit.cpp @@ -1,7 +1,8 @@ // jit.cpp -// Copyright (c) 2017, zhiayang@gmail.com +// Copyright (c) 2017, zhiayang // Licensed under the Apache License Version 2.0. +#include "errors.h" #include "backends/llvm.h" namespace backend @@ -55,9 +56,18 @@ namespace backend llvm::JITTargetAddress LLVMJit::getSymbolAddress(const std::string& name) { - return llvm::cantFail(this->findSymbol(name).getAddress()); - } + auto addr = this->findSymbol(name).getAddress(); + if(!addr) + { + std::string err; + auto out = llvm::raw_string_ostream(err); + + out << addr.takeError(); + error("llvm: failed to find symbol '%s' (%s)", name, out.str()); + } + return addr.get(); + } } diff --git a/source/backend/llvm/linker.cpp b/source/backend/llvm/linker.cpp index 854802da..de456387 100644 --- a/source/backend/llvm/linker.cpp +++ b/source/backend/llvm/linker.cpp @@ -1,5 +1,5 @@ // linker.cpp -// Copyright (c) 2014 - 2016, zhiayang@gmail.com +// Copyright (c) 2014 - 2016, zhiayang // Licensed under the Apache License Version 2.0. @@ -17,6 +17,7 @@ #ifdef _MSC_VER #pragma warning(push, 0) + #pragma warning(disable: 4267) #endif #include "llvm/IR/Verifier.h" @@ -59,7 +60,7 @@ #include "frontend.h" #include "backends/llvm.h" -#include "tinyprocesslib/process.h" +#include "tinyprocesslib/tinyprocess.h" static llvm::LLVMContext globalContext; @@ -67,9 +68,12 @@ static llvm::LLVMContext globalContext; template static void _printTiming(T ts, const std::string& thing) { - auto dur = std::chrono::high_resolution_clock::now() - ts; - auto ms = (double) dur.count() / 1000.0 / 1000.0; - printf("%s took %.1f ms%s\n", thing.c_str(), ms, ms > 3000 ? strprintf(" (aka %.2f s)", ms / 1000.0).c_str() : ""); + if(frontend::getPrintProfileStats()) + { + auto dur = std::chrono::high_resolution_clock::now() - ts; + auto ms = (double) dur.count() / 1000.0 / 1000.0; + printf("%s took %.1f ms%s\n", thing.c_str(), ms, ms > 3000 ? strprintf(" (aka %.2f s)", ms / 1000.0).c_str() : ""); + } } namespace backend @@ -81,8 +85,6 @@ namespace backend LLVMBackend::LLVMBackend(CompiledData& dat, std::vector inputs, std::string output) : Backend(BackendCaps::EmitAssembly | BackendCaps::EmitObject | BackendCaps::EmitProgram | BackendCaps::JIT, dat, inputs, output) { - if(inputs.size() != 1) - error("Need exactly 1 input filename, have %zu", inputs.size()); } std::string LLVMBackend::str() @@ -96,26 +98,18 @@ namespace backend llvm::InitializeNativeTarget(); - // fprintf(stderr, "%s\n\n\n", this->compiledData.module->print().c_str()); - auto mainModule = this->translateFIRtoLLVM(this->compiledData.module); - auto s = frontend::getFilenameFromPath(this->inputFilenames[0]); if(this->compiledData.module->getEntryFunction()) this->entryFunction = mainModule->getFunction(this->compiledData.module->getEntryFunction()->getName().mangled()); - this->linkedModule = std::unique_ptr(mainModule); - this->finaliseGlobalConstructors(); // ok, move some shit into here because llvm is fucking retarded this->setupTargetMachine(); this->linkedModule->setDataLayout(this->targetMachine->createDataLayout()); - if(frontend::getPrintLLVMIR()) - this->linkedModule->print(llvm::outs(), 0); - - _printTiming(ts, "translation to llvm"); + _printTiming(ts, "llvm translation"); } @@ -129,7 +123,7 @@ namespace backend fprintf(stderr, "\n\n"); this->linkedModule->print(llvm::errs(), 0); - BareError::make("\nLLVM Module verification failed\n")->postAndQuit(); + BareError::make("llvm: module verification failed")->postAndQuit(); } llvm::legacy::PassManager fpm = llvm::legacy::PassManager(); @@ -182,6 +176,9 @@ namespace backend fpm.run(*this->linkedModule); _printTiming(ts, "llvm opt"); + + if(frontend::getPrintLLVMIR()) + this->linkedModule->print(llvm::outs(), 0); } void LLVMBackend::writeOutput() @@ -191,7 +188,7 @@ namespace backend // verify the module. { if(llvm::verifyModule(*this->linkedModule, &llvm::errs())) - error("\n\nLLVM Module verification failed"); + error("llvm: module verification failed"); } std::string foldername; @@ -205,7 +202,7 @@ namespace backend if(frontend::getOutputMode() == ProgOutputMode::RunJit) { - const char* argv = ("__JIT-" + this->linkedModule->getModuleIdentifier()).c_str(); + const char* argv = ("llvm-jit-" + this->linkedModule->getModuleIdentifier()).c_str(); auto entry = this->getEntryFunctionFromJIT(); _printTiming(ts, "llvm jit"); @@ -231,7 +228,7 @@ namespace backend { if(frontend::getOutputMode() != ProgOutputMode::ObjectFile && !this->compiledData.module->getEntryFunction()) { - error("No entry function marked, a program cannot be compiled"); + error("llvm: no entry function marked, a program cannot be compiled"); } auto buffer = this->initialiseLLVMStuff(); @@ -250,8 +247,8 @@ namespace backend auto fd = platform::openFile(objname.c_str(), O_RDWR | O_CREAT, 0); if(fd == platform::InvalidFileHandle) { - perror("open(2) error"); - BareError::make("Unable to create temporary file ('%s') for linking", objname)->postAndQuit(); + perror("llvm: open(2) error"); + BareError::make("llvm: unable to create temporary file ('%s') for linking", objname)->postAndQuit(); } platform::writeFile(fd, buffer.data(), buffer.size_in_bytes()); @@ -278,7 +275,8 @@ namespace backend size_t num_extra = 0; size_t s = 5 + num_extra + (2 * libs.size()) + (2 * libdirs.size()) + (2 * frames.size()) + (2 * framedirs.size()); - const char** argv = new const char*[s]; + const char** argv = (const char**) malloc(s * sizeof(const char*)); + memset(argv, 0, s * sizeof(const char*)); argv[0] = "cc"; @@ -339,7 +337,7 @@ namespace backend }); - delete[] argv; + free(argv); if(status != 0 || output.size() != 0) { @@ -380,7 +378,7 @@ namespace backend const llvm::Target* theTarget = llvm::TargetRegistry::lookupTarget("", targetTriple, err_str); if(!theTarget) { - error("failed in creating target: (wanted: '%s'); llvm error: %s\n", targetTriple.str(), err_str); + error("llvm: failed in creating target: (wanted: '%s'); llvm error: %s\n", targetTriple.str(), err_str); } @@ -408,7 +406,7 @@ namespace backend } else { - error("Invalid mcmodel '%s' (valid options: kernel, small, medium, or large)", frontend::getParameter("mcmodel")); + error("llvm: invalid mcmodel '%s' (valid options: kernel, small, medium, or large)", frontend::getParameter("mcmodel")); } @@ -440,7 +438,6 @@ namespace backend llvm::raw_pwrite_stream* rawStream = bufferStream.get(); { - // auto p = prof::Profile(PROFGROUP_LLVM, "llvm_emit_object"); llvm::legacy::PassManager pm = llvm::legacy::PassManager(); targetMachine->addPassesToEmitFile(pm, *rawStream, rawStream, llvm::TargetMachine::CodeGenFileType::CGFT_ObjectFile); pm.run(*this->linkedModule); @@ -471,10 +468,6 @@ namespace backend EntryPoint_t LLVMBackend::getEntryFunctionFromJIT() { - // all linked already. - // dump here, before the output. - - // if JIT-ing, we need to load all the framework shit. // note(compat): i think -L -l ordering matters when resolving libraries // we don't support that right now. @@ -557,7 +550,7 @@ namespace backend #if defined(__MACH__) ext = ".dylib"; - #elif defined(WIN32) + #elif defined(_WIN32) ext = ".dll"; #else ext = ".so"; @@ -566,7 +559,7 @@ namespace backend std::string err; llvm::sys::DynamicLibrary dl = llvm::sys::DynamicLibrary::getPermanentLibrary(("lib" + l + ext).c_str(), &err); if(!dl.isValid()) - error("Failed to load library '%s', dlopen failed with error:\n%s", l, err); + error("llvm: failed to load library '%s', dlopen failed with error:\n%s", l, err); } @@ -577,40 +570,26 @@ namespace backend std::string err; llvm::sys::DynamicLibrary dl = llvm::sys::DynamicLibrary::getPermanentLibrary(name.c_str(), &err); if(!dl.isValid()) - error("Failed to load framework '%s', dlopen failed with error:\n%s", l, err); + error("llvm: failed to load framework '%s', dlopen failed with error:\n%s", l, err); } EntryPoint_t ret = 0; if(this->entryFunction) { - #if 1 auto name = this->entryFunction->getName().str(); this->jitInstance = new LLVMJit(this->targetMachine); this->jitInstance->addModule(std::move(this->linkedModule)); + // this->jitInstance-> auto entryaddr = this->jitInstance->getSymbolAddress(name); ret = (int (*)(int, const char**)) entryaddr; iceAssert(ret && "failed to resolve entry function address"); - - #else - llvm::ExecutionEngine* execEngine = llvm::EngineBuilder(std::unique_ptr(this->linkedModule.get())) - .create(this->targetMachine); - - // finalise the object, which does something. - iceAssert(execEngine); - execEngine->finalizeObject(); - - void* func = execEngine->getPointerToFunction(this->entryFunction); - iceAssert(func != 0); - - ret = (int (*)(int, const char**)) func; - #endif } else { - error("No entry function marked, cannot JIT"); + error("llvm: no entry function marked, cannot JIT"); } @@ -628,122 +607,8 @@ namespace backend } #endif - // ret(1, (const char**) &"JIT"); return ret; } - - - - - - - - - - - - - - - - void LLVMBackend::finaliseGlobalConstructors() - { - // first, if our entry function is named 'main', then all is well - // if not, then we need to make our own main (checking for conflicts) and call the real entry - // function there. - - // this->linkedModule->print(llvm::errs(), 0); - - - llvm::IRBuilder<> builder(LLVMBackend::getLLVMContext()); - - auto entryfunc = this->entryFunction; - if(!entryfunc) - { - // keep trying various things. - std::vector trymains = { "main", "_FFfoo4main_FAv" }; - for(const auto& m : trymains) - { - entryfunc = this->linkedModule->getFunction(m); - if(entryfunc) break; - } - - if(entryfunc) - this->entryFunction = entryfunc; - - else - error("No entry point marked with '@entry', and no 'main' function; cannot compile program"); - } - - if(entryfunc->getName() != "main") - { - // well. - if(this->linkedModule->getFunction("main") != 0) - { - error("Conflicting 'main' function; entry function was '%s', but 'main' must be undefined in order to allow trampoline code to work (blame the linker)"); - } - - // ok. - - auto& c = LLVMBackend::getLLVMContext(); - llvm::FunctionType* ft = llvm::FunctionType::get(llvm::Type::getInt32Ty(c), - { llvm::Type::getInt32Ty(c), llvm::Type::getInt8Ty(c)->getPointerTo()->getPointerTo() }, false); - - llvm::Function* mainf = llvm::Function::Create(ft, llvm::GlobalValue::ExternalLinkage, "main", this->linkedModule.get()); - llvm::BasicBlock* entry = llvm::BasicBlock::Create(c, "main_entry", mainf); - builder.SetInsertPoint(entry); - - auto argc = mainf->arg_begin(); - auto argv = mainf->arg_begin() + 1; - - builder.SetInsertPoint(entry); - llvm::Value* returnVal = 0; - if(entryfunc->getFunctionType()->params() == ft->params()) - { - // ok, can pass arguments - returnVal = builder.CreateCall(entryfunc->getFunctionType(), entryfunc, { argc, argv }); - } - else - { - returnVal = builder.CreateCall(entryfunc->getFunctionType(), entryfunc, { }); - } - - - // ok, check return type - iceAssert(returnVal); - { - if(entryfunc->getReturnType()->isIntegerTy()) - { - returnVal = builder.CreateIntCast(returnVal, llvm::Type::getInt32Ty(c), true); - builder.CreateRet(returnVal); - } - else - { - builder.CreateRet(llvm::ConstantInt::getSigned(llvm::Type::getInt32Ty(c), 0)); - } - } - } - - // return; - { - // insert a call at the beginning of main(). - if((frontend::getOutputMode() == ProgOutputMode::Program || frontend::getOutputMode() == ProgOutputMode::RunJit) && !entryfunc) - error("No entry function defined"); - - - llvm::BasicBlock* entryblock = &entryfunc->getEntryBlock(); - llvm::BasicBlock* f = llvm::BasicBlock::Create(LLVMBackend::getLLVMContext(), "__entry_entry", entryfunc); - - f->moveBefore(entryblock); - - builder.SetInsertPoint(f); - builder.CreateCall(this->linkedModule->getFunction("__global_init_function__")); - - // auto x = builder.CreateAlloca(llvm::Type::getInt64Ty(LLVMBackend::getLLVMContext()), nullptr, "x"); - // builder.CreateStore(llvm::ConstantInt::getSigned(llvm::Type::getInt64Ty(LLVMBackend::getLLVMContext()), 491), x, true); - builder.CreateBr(entryblock); - } - } } diff --git a/source/backend/llvm/translator.cpp b/source/backend/llvm/translator.cpp index 30835ddb..5b6643fc 100644 --- a/source/backend/llvm/translator.cpp +++ b/source/backend/llvm/translator.cpp @@ -1,5 +1,5 @@ // Translator.cpp -// Copyright (c) 2014 - 2016, zhiayang@gmail.com +// Copyright (c) 2014 - 2016, zhiayang // Licensed under the Apache License Version 2.0. @@ -68,6 +68,11 @@ namespace backend return str; } + static llvm::Type* getNativeWordTy() + { + auto& gc = LLVMBackend::getLLVMContext(); + return llvm::IntegerType::getIntNTy(gc, (unsigned int) fir::getNativeWordSizeInBits()); + } static llvm::Type* typeToLlvm(fir::Type* type, llvm::Module* mod) { @@ -129,21 +134,9 @@ namespace backend // to allow recursion, declare the type first. createdTypes[ct->getTypeName()] = llvm::StructType::create(gc, ct->getTypeName().mangled()); - std::vector lmems; - - std::function*)> addMembers - = [&addMembers, &mod](fir::ClassType* cls, std::vector* mems) -> auto { - - if(!cls) - return; - - addMembers(cls->getBaseClass(), mems); - - for(auto f : cls->getElements()) - mems->push_back(typeToLlvm(f, mod)); - }; - - addMembers(ct, &lmems); + std::vector lmems = util::map(ct->getAllElementsIncludingBase(), [&mod](auto t) -> auto { + return typeToLlvm(t, mod); + }); // insert the vtable at the front. lmems.insert(lmems.begin(), llvm::Type::getInt8PtrTy(gc)); @@ -199,18 +192,17 @@ namespace backend std::vector mems(4); mems[SAA_DATA_INDEX] = typeToLlvm(llat->getElementType()->getPointerTo(), mod); - mems[SAA_LENGTH_INDEX] = llvm::IntegerType::getInt64Ty(gc); - mems[SAA_CAPACITY_INDEX] = llvm::IntegerType::getInt64Ty(gc); - mems[SAA_REFCOUNTPTR_INDEX] = llvm::IntegerType::getInt64PtrTy(gc); + mems[SAA_LENGTH_INDEX] = getNativeWordTy(); + mems[SAA_CAPACITY_INDEX] = getNativeWordTy(); + mems[SAA_REFCOUNTPTR_INDEX] = getNativeWordTy()->getPointerTo(); return llvm::StructType::get(gc, mems, false); } else if(type->isStringType()) { llvm::Type* i8ptrtype = llvm::Type::getInt8PtrTy(gc); - llvm::Type* i64type = llvm::Type::getInt64Ty(gc); - auto id = Identifier("__string", IdKind::Type); + auto id = util::obfuscateIdentifier("string", IdKind::Type); if(createdTypes.find(id) != createdTypes.end()) return createdTypes[id]; @@ -218,9 +210,9 @@ namespace backend std::vector mems(4); mems[SAA_DATA_INDEX] = i8ptrtype; - mems[SAA_LENGTH_INDEX] = i64type; - mems[SAA_CAPACITY_INDEX] = i64type; - mems[SAA_REFCOUNTPTR_INDEX] = i64type->getPointerTo(); + mems[SAA_LENGTH_INDEX] = getNativeWordTy(); + mems[SAA_CAPACITY_INDEX] = getNativeWordTy(); + mems[SAA_REFCOUNTPTR_INDEX] = getNativeWordTy()->getPointerTo(); auto str = llvm::StructType::create(gc, id.mangled()); str->setBody(mems); @@ -233,7 +225,7 @@ namespace backend std::vector mems(2); mems[SLICE_DATA_INDEX] = typeToLlvm(slct->getElementType()->getPointerTo(), mod); - mems[SLICE_LENGTH_INDEX] = llvm::IntegerType::getInt64Ty(gc); + mems[SLICE_LENGTH_INDEX] = getNativeWordTy(); return llvm::StructType::get(gc, mems, false); } @@ -243,38 +235,35 @@ namespace backend } else if(type->isRangeType()) { - llvm::Type* i64type = llvm::Type::getInt64Ty(gc); - - auto id = Identifier("__range", IdKind::Type); + auto id = util::obfuscateIdentifier("range", IdKind::Type); if(createdTypes.find(id) != createdTypes.end()) return createdTypes[id]; auto str = llvm::StructType::create(gc, id.mangled()); - str->setBody({ i64type, i64type, i64type }); + str->setBody({ getNativeWordTy(), getNativeWordTy(), getNativeWordTy() }); return createdTypes[id] = str; } else if(type->isEnumType()) { std::vector mems; - mems.push_back(llvm::IntegerType::getInt64Ty(gc)); + mems.push_back(getNativeWordTy()); mems.push_back(typeToLlvm(type->toEnumType()->getCaseType(), mod)); return llvm::StructType::get(gc, mems, false); } else if(type->isAnyType()) { - llvm::Type* i64type = llvm::Type::getInt64Ty(gc); llvm::Type* arrtype = llvm::ArrayType::get(llvm::Type::getInt8Ty(gc), BUILTIN_ANY_DATA_BYTECOUNT); - auto id = Identifier("__any", IdKind::Type); + auto id = util::obfuscateIdentifier("any", IdKind::Type); if(createdTypes.find(id) != createdTypes.end()) return createdTypes[id]; auto str = llvm::StructType::create(gc, id.mangled()); // typeid (+ highest-bit-mask), refcount, data. - str->setBody({ i64type, i64type->getPointerTo(), arrtype }); + str->setBody({ getNativeWordTy(), getNativeWordTy()->getPointerTo(), arrtype }); return createdTypes[id] = str; } @@ -285,8 +274,6 @@ namespace backend if(createdTypes.find(ut->getTypeName()) != createdTypes.end()) return createdTypes[ut->getTypeName()]; - - llvm::Type* i64type = llvm::Type::getInt64Ty(gc); auto dl = llvm::DataLayout(mod); size_t maxSz = 0; @@ -299,12 +286,12 @@ namespace backend if(maxSz > 0) { createdTypes[ut->getTypeName()] = llvm::StructType::create(gc, { - i64type, llvm::ArrayType::get(llvm::Type::getInt8Ty(gc), maxSz) + getNativeWordTy(), llvm::ArrayType::get(llvm::Type::getInt8Ty(gc), maxSz) }, ut->getTypeName().mangled()); } else { - createdTypes[ut->getTypeName()] = llvm::StructType::create(gc, { i64type }, ut->getTypeName().mangled()); + createdTypes[ut->getTypeName()] = llvm::StructType::create(gc, { getNativeWordTy() }, ut->getTypeName().mangled()); } return createdTypes[ut->getTypeName()]; @@ -324,19 +311,18 @@ namespace backend iceAssert(maxSz > 0); createdTypes[ut->getTypeName()] = llvm::StructType::create(gc, { - // llvm::ArrayType::get(llvm::Type::getInt8Ty(gc), maxSz) - llvm::IntegerType::getIntNTy(gc, maxSz * CHAR_BIT) + llvm::IntegerType::getIntNTy(gc, (unsigned int) (maxSz * CHAR_BIT)) }, ut->getTypeName().mangled()); return createdTypes[ut->getTypeName()]; } else if(type->isPolyPlaceholderType()) { - error("llvm: Unfulfilled polymorphic placeholder type '%s'", type); + error("llvm: unfulfilled polymorphic placeholder type '%s'", type); } else { - error("llvm: Unimplememented type '%s' for LLVM backend", type); + error("llvm: unimplememented type '%s' for LLVM backend", type); } } @@ -376,15 +362,8 @@ namespace backend size_t i = 0; for(auto it = func->arg_begin(); it != func->arg_end(); it++, i++) - { valueMap[ffn->getArguments()[i]->id] = it; - // fprintf(stderr, "adding func arg %zu\n", ffn->getArguments()[i]->id); - } - - // sort the blocklist first - std::sort(ffn->getBlockList().begin(), ffn->getBlockList().end(), [](fir::IRBlock* a, fir::IRBlock* b) -> bool { - return a->id < b->id; }); for(auto b : ffn->getBlockList()) { @@ -434,8 +413,6 @@ namespace backend } else if(auto ca = dcast(fir::ConstantArray, c)) { - // auto p = prof::Profile(PROFGROUP_LLVM, "const array"); - auto arrt = llvm::cast(typeToLlvm(ca->getType(), mod)); std::vector vals; @@ -458,8 +435,6 @@ namespace backend } else if(auto ct = dcast(fir::ConstantTuple, c)) { - // auto p = prof::Profile(PROFGROUP_LLVM, "const tuple"); - std::vector vals; vals.reserve(ct->getValues().size()); @@ -478,7 +453,6 @@ namespace backend } else if(auto cs = dcast(fir::ConstantString, c)) { - // auto p = prof::Profile(PROFGROUP_LLVM, "const string"); size_t origLen = cs->getValue().length(); std::string str = cs->getValue(); @@ -486,23 +460,14 @@ namespace backend llvm::GlobalVariable* gv = new llvm::GlobalVariable(*mod, cstr->getType(), true, llvm::GlobalValue::LinkageTypes::InternalLinkage, cstr, "_FV_STR_" + std::to_string(cs->id)); - auto zconst = llvm::ConstantInt::get(llvm::Type::getInt64Ty(LLVMBackend::getLLVMContext()), 0); + auto zconst = llvm::ConstantInt::get(getNativeWordTy(), 0); std::vector indices = { zconst, zconst }; llvm::Constant* gepd = llvm::ConstantExpr::getGetElementPtr(gv->getType()->getPointerElementType(), gv, indices); - //! ACHTUNG ! - //????? WTF IS THIS??? - // seems like a relic from when we were doing the refcount-behind-string-data thing - // surprised we never hit a bug due to this, but i'm 99.99999% sure this is wrong. - /* - auto eightconst = llvm::ConstantInt::get(llvm::Type::getInt64Ty(LLVMBackend::getLLVMContext()), 8); - gepd = llvm::ConstantExpr::getInBoundsGetElementPtr(gepd->getType()->getPointerElementType(), gepd, eightconst); - */ - - auto len = llvm::ConstantInt::get(llvm::Type::getInt64Ty(LLVMBackend::getLLVMContext()), origLen); + auto len = llvm::ConstantInt::get(getNativeWordTy(), origLen); iceAssert(gepd->getType() == llvm::Type::getInt8PtrTy(LLVMBackend::getLLVMContext())); - iceAssert(len->getType() == llvm::Type::getInt64Ty(LLVMBackend::getLLVMContext())); + iceAssert(len->getType() == getNativeWordTy()); std::vector mems = { gepd, len }; auto ret = llvm::ConstantStruct::get(llvm::cast(typeToLlvm(fir::Type::getCharSlice(false), mod)), mems); @@ -537,12 +502,12 @@ namespace backend llvm::GlobalVariable* tmpglob = new llvm::GlobalVariable(*mod, constArray->getType(), false, llvm::GlobalValue::LinkageTypes::InternalLinkage, constArray, "_FV_ARR_" + std::to_string(cda->id)); - auto zconst = llvm::ConstantInt::get(llvm::Type::getInt64Ty(LLVMBackend::getLLVMContext()), 0); + auto zconst = llvm::ConstantInt::get(getNativeWordTy(), 0); std::vector indices = { zconst, zconst }; llvm::Constant* gepd = llvm::ConstantExpr::getGetElementPtr(tmpglob->getType()->getPointerElementType(), tmpglob, indices); - auto flen = fir::ConstantInt::getInt64(cda->getArray()->getType()->toArrayType()->getArraySize()); - auto fcap = fir::ConstantInt::getInt64(-1); + auto flen = fir::ConstantInt::getNative(cda->getArray()->getType()->toArrayType()->getArraySize()); + auto fcap = fir::ConstantInt::getNative(-1); std::vector mems = { gepd, constToLlvm(flen, valueMap, mod), constToLlvm(fcap, valueMap, mod), zconst }; auto ret = llvm::ConstantStruct::get(llvm::cast(typeToLlvm(cda->getType(), mod)), mems); @@ -551,7 +516,7 @@ namespace backend else { std::vector mems = { constToLlvm(cda->getData(), valueMap, mod), constToLlvm(cda->getLength(), valueMap, mod), - constToLlvm(cda->getCapacity(), valueMap, mod), llvm::ConstantInt::get(llvm::Type::getInt64Ty(LLVMBackend::getLLVMContext()), 0) }; + constToLlvm(cda->getCapacity(), valueMap, mod), llvm::ConstantInt::get(getNativeWordTy(), 0) }; auto ret = llvm::ConstantStruct::get(llvm::cast(typeToLlvm(cda->getType(), mod)), mems); return cachedConstants[c] = ret; @@ -563,7 +528,7 @@ namespace backend } else if(dcast(fir::ConstantStruct, c)) { - _error_and_exit("notsup const struct"); + _error_and_exit("notsup const struct\n"); } else if(auto it = valueMap.find(c->id); it != valueMap.end() && llvm::isa(it->second)) { @@ -583,7 +548,6 @@ namespace backend llvm::Module* LLVMBackend::translateFIRtoLLVM(fir::Module* firmod) { auto& gc = LLVMBackend::getLLVMContext(); - // fprintf(stderr, "\n%s\n", firmod->print().c_str()); llvm::Module* module = new llvm::Module(firmod->getModuleName(), LLVMBackend::getLLVMContext()); llvm::IRBuilder<> builder(gc); @@ -679,7 +643,7 @@ namespace backend llvm::Constant* gv = new llvm::GlobalVariable(*module, cstr->getType(), true, llvm::GlobalValue::LinkageTypes::InternalLinkage, cstr, id); - auto zconst = llvm::ConstantInt::get(llvm::Type::getInt64Ty(gc), 0); + auto zconst = llvm::ConstantInt::get(getNativeWordTy(), 0); std::vector ix { zconst, zconst }; gv = llvm::ConstantExpr::getInBoundsGetElementPtr(gv->getType()->getPointerElementType(), gv, ix); @@ -721,27 +685,27 @@ namespace backend if(intr.first.str() == "memcpy") { llvm::FunctionType* ft = llvm::FunctionType::get(llvm::Type::getVoidTy(gc), { llvm::Type::getInt8PtrTy(gc), - llvm::Type::getInt8PtrTy(gc), llvm::Type::getInt64Ty(gc), llvm::Type::getInt1Ty(gc) }, false); - fn = module->getOrInsertFunction("llvm.memcpy.p0i8.p0i8.i64", ft); + llvm::Type::getInt8PtrTy(gc), getNativeWordTy(), llvm::Type::getInt1Ty(gc) }, false); + fn = module->getOrInsertFunction(strprintf("llvm.memcpy.p0i8.p0i8.i%d", fir::getNativeWordSizeInBits()), ft); } else if(intr.first.str() == "memmove") { llvm::FunctionType* ft = llvm::FunctionType::get(llvm::Type::getVoidTy(gc), { llvm::Type::getInt8PtrTy(gc), - llvm::Type::getInt8PtrTy(gc), llvm::Type::getInt64Ty(gc), llvm::Type::getInt1Ty(gc) }, false); - fn = module->getOrInsertFunction("llvm.memmove.p0i8.p0i8.i64", ft); + llvm::Type::getInt8PtrTy(gc), getNativeWordTy(), llvm::Type::getInt1Ty(gc) }, false); + fn = module->getOrInsertFunction(strprintf("llvm.memmove.p0i8.p0i8.i%d", fir::getNativeWordSizeInBits()), ft); } else if(intr.first.str() == "memset") { llvm::FunctionType* ft = llvm::FunctionType::get(llvm::Type::getVoidTy(gc), { llvm::Type::getInt8PtrTy(gc), - llvm::Type::getInt8Ty(gc), llvm::Type::getInt64Ty(gc), llvm::Type::getInt1Ty(gc) }, false); - fn = module->getOrInsertFunction("llvm.memset.p0i8.i64", ft); + llvm::Type::getInt8Ty(gc), getNativeWordTy(), llvm::Type::getInt1Ty(gc) }, false); + fn = module->getOrInsertFunction(strprintf("llvm.memset.p0i8.i%d", fir::getNativeWordSizeInBits()), ft); } else if(intr.first.str() == "memcmp") { // in line with the rest, take 4 arguments. (this is our own "intrinsic") llvm::FunctionType* ft = llvm::FunctionType::get(llvm::Type::getInt32Ty(gc), { llvm::Type::getInt8PtrTy(gc), - llvm::Type::getInt8PtrTy(gc), llvm::Type::getInt64Ty(gc), llvm::Type::getInt1Ty(gc) }, false); + llvm::Type::getInt8PtrTy(gc), getNativeWordTy(), llvm::Type::getInt1Ty(gc) }, false); fn = llvm::Function::Create(ft, llvm::GlobalValue::LinkageTypes::InternalLinkage, "fir.intrinsic.memcmp", module); llvm::Function* func = llvm::cast(fn); @@ -789,7 +753,7 @@ namespace backend auto zeroconst = llvm::ConstantInt::get(gc, llvm::APInt(64, 0, true)); auto zeroconst8 = llvm::ConstantInt::get(gc, llvm::APInt(8, 0, true)); - llvm::Value* ctr = builder.CreateAlloca(llvm::Type::getInt64Ty(gc)); + llvm::Value* ctr = builder.CreateAlloca(getNativeWordTy()); builder.CreateStore(zeroconst, ctr); llvm::BasicBlock* loopcond = llvm::BasicBlock::Create(gc, "loopcond", func); @@ -830,7 +794,7 @@ namespace backend } else if(intr.first.name == "roundup_pow2") { - llvm::FunctionType* ft = llvm::FunctionType::get(llvm::Type::getInt64Ty(gc), { llvm::Type::getInt64Ty(gc) }, false); + llvm::FunctionType* ft = llvm::FunctionType::get(getNativeWordTy(), { getNativeWordTy() }, false); fn = llvm::Function::Create(ft, llvm::GlobalValue::LinkageTypes::InternalLinkage, "fir.intrinsic.roundup_pow2", module); llvm::Function* func = llvm::cast(fn); @@ -859,8 +823,8 @@ namespace backend return ret; */ - llvm::Value* num = builder.CreateAlloca(llvm::Type::getInt64Ty(gc)); - llvm::Value* retval = builder.CreateAlloca(llvm::Type::getInt64Ty(gc)); + llvm::Value* num = builder.CreateAlloca(getNativeWordTy()); + llvm::Value* retval = builder.CreateAlloca(getNativeWordTy()); auto oneconst = llvm::ConstantInt::get(gc, llvm::APInt(64, 1, true)); auto zeroconst = llvm::ConstantInt::get(gc, llvm::APInt(64, 0, true)); @@ -1411,8 +1375,8 @@ namespace backend if(sgn) r2 = builder.CreateICmpSLE(a, b); else r2 = builder.CreateICmpULE(a, b); - r1 = builder.CreateIntCast(r1, llvm::Type::getInt64Ty(gc), false); - r2 = builder.CreateIntCast(r2, llvm::Type::getInt64Ty(gc), false); + r1 = builder.CreateIntCast(r1, getNativeWordTy(), false); + r2 = builder.CreateIntCast(r2, getNativeWordTy(), false); llvm::Value* ret = builder.CreateSub(r1, r2); addValueToMap(ret, inst->realOutput); @@ -1429,8 +1393,8 @@ namespace backend llvm::Value* r1 = builder.CreateFCmpOGE(a, b); llvm::Value* r2 = builder.CreateFCmpOLE(a, b); - r1 = builder.CreateIntCast(r1, llvm::Type::getInt64Ty(gc), false); - r2 = builder.CreateIntCast(r2, llvm::Type::getInt64Ty(gc), false); + r1 = builder.CreateIntCast(r1, getNativeWordTy(), false); + r2 = builder.CreateIntCast(r2, getNativeWordTy(), false); llvm::Value* ret = builder.CreateSub(r1, r2); addValueToMap(ret, inst->realOutput); @@ -1553,19 +1517,6 @@ namespace backend break; } - case fir::OpKind::Value_StackAlloc: - { - iceAssert(inst->operands.size() == 1); - fir::Type* ft = inst->operands[0]->getType(); - llvm::Type* t = typeToLlvm(ft, module); - - llvm::Value* ret = builder.CreateAlloca(t); - builder.CreateStore(llvm::Constant::getNullValue(t), ret); - - addValueToMap(ret, inst->realOutput); - break; - } - case fir::OpKind::Value_CreatePHI: { iceAssert(inst->operands.size() == 1); @@ -1636,7 +1587,7 @@ namespace backend case fir::OpKind::Value_CallVirtualMethod: { - // args are: 0. class, 1. index, 2. functiontype, 3...N args + // args are: 0. classtype, 1. index, 2. functiontype, 3...N args auto clsty = inst->operands[0]->getType()->toClassType(); iceAssert(clsty); @@ -1870,7 +1821,7 @@ namespace backend iceAssert(t); llvm::Value* gep = builder.CreateConstGEP1_64(llvm::ConstantPointerNull::get(t->getPointerTo()), 1); - gep = builder.CreatePtrToInt(gep, llvm::Type::getInt64Ty(gc)); + gep = builder.CreatePtrToInt(gep, getNativeWordTy()); addValueToMap(gep, inst->realOutput); break; @@ -1895,38 +1846,6 @@ namespace backend break; } - case fir::OpKind::Value_PointerAddition: - { - iceAssert(inst->operands.size() == 2); - - llvm::Value* a = getOperand(inst, 0); - llvm::Value* b = getOperand(inst, 1); - - iceAssert(a->getType()->isPointerTy()); - iceAssert(b->getType()->isIntegerTy()); - - llvm::Value* ret = builder.CreateInBoundsGEP(a, b); - addValueToMap(ret, inst->realOutput); - break; - } - - case fir::OpKind::Value_PointerSubtraction: - { - iceAssert(inst->operands.size() == 2); - - llvm::Value* a = getOperand(inst, 0); - llvm::Value* b = getOperand(inst, 1); - - iceAssert(a->getType()->isPointerTy()); - iceAssert(b->getType()->isIntegerTy()); - - llvm::Value* negb = builder.CreateNeg(b); - llvm::Value* ret = builder.CreateInBoundsGEP(a, negb); - addValueToMap(ret, inst->realOutput); - break; - } - - case fir::OpKind::Value_InsertValue: @@ -2283,7 +2202,7 @@ namespace backend - + case fir::OpKind::Value_StackAlloc: case fir::OpKind::Value_CreateLVal: { iceAssert(inst->operands.size() == 1); diff --git a/source/backend/x64AsmBackend.cpp b/source/backend/x64AsmBackend.cpp index 8b3dc55f..ab70b257 100644 --- a/source/backend/x64AsmBackend.cpp +++ b/source/backend/x64AsmBackend.cpp @@ -1,5 +1,5 @@ // x64AsmBackend.cpp -// Copyright (c) 2014 - 2016, zhiayang@gmail.com +// Copyright (c) 2014 - 2016, zhiayang // Licensed under the Apache License Version 2.0. #include "backend.h" @@ -13,17 +13,17 @@ namespace backend void x64Backend::performCompilation() { - _error_and_exit("enotsup"); + _error_and_exit("enotsup\n"); } void x64Backend::optimiseProgram() { - _error_and_exit("enotsup"); + _error_and_exit("enotsup\n"); } void x64Backend::writeOutput() { - _error_and_exit("enotsup"); + _error_and_exit("enotsup\n"); } std::string x64Backend::str() diff --git a/source/codegen/alloc.cpp b/source/codegen/alloc.cpp index ad9492cf..6d9edaca 100644 --- a/source/codegen/alloc.cpp +++ b/source/codegen/alloc.cpp @@ -1,5 +1,5 @@ // alloc.cpp -// Copyright (c) 2017, zhiayang@gmail.com +// Copyright (c) 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "sst.h" @@ -7,19 +7,17 @@ #include "platform.h" #include "gluecode.h" -#define BUILTIN_ALLOC_CHECK_NEGATIVE_LENGTH_NAME "__alloc_checkneg" - - static fir::Function* getCheckNegativeLengthFunction(cgn::CodegenState* cs) { - fir::Function* checkf = cs->module->getFunction(Identifier(BUILTIN_ALLOC_CHECK_NEGATIVE_LENGTH_NAME, IdKind::Name)); + auto fname = util::obfuscateIdentifier("alloc_checkneg"); + fir::Function* checkf = cs->module->getFunction(fname); if(!checkf) { auto restore = cs->irb.getCurrentBlock(); - fir::Function* func = cs->module->getOrCreateFunction(Identifier(BUILTIN_ALLOC_CHECK_NEGATIVE_LENGTH_NAME, IdKind::Name), - fir::FunctionType::get({ fir::Type::getInt64(), fir::Type::getCharSlice(false) }, fir::Type::getVoid()), fir::LinkageType::Internal); + fir::Function* func = cs->module->getOrCreateFunction(fname, + fir::FunctionType::get({ fir::Type::getNativeWord(), fir::Type::getCharSlice(false) }, fir::Type::getVoid()), fir::LinkageType::Internal); func->setAlwaysInline(); @@ -33,13 +31,13 @@ static fir::Function* getCheckNegativeLengthFunction(cgn::CodegenState* cs) fir::IRBlock* merge = cs->irb.addNewBlockInFunction("merge", func); // make the thing - auto isNeg = cs->irb.ICmpLT(s1, fir::ConstantInt::getInt64(0)); + auto isNeg = cs->irb.ICmpLT(s1, fir::ConstantInt::getNative(0)); cs->irb.CondBranch(isNeg, failb, merge); cs->irb.setCurrentBlock(failb); { - cgn::glue::printRuntimeError(cs, s2, "Tried to allocate a negative ('%ld') amount of memory\n", { s1 }); + cgn::glue::printRuntimeError(cs, s2, "tried to allocate a negative ('%ld') amount of memory\n", { s1 }); } cs->irb.setCurrentBlock(merge); @@ -73,7 +71,7 @@ static fir::Value* performAllocation(cgn::CodegenState* cs, sst::AllocOp* alloc, { auto arrp = ptr; - auto ctrp = cs->irb.CreateLValue(fir::Type::getInt64()); + auto ctrp = cs->irb.CreateLValue(fir::Type::getNativeWord()); auto actuallyStore = [cs, type, alloc](fir::Value* ptr) -> void { @@ -118,14 +116,14 @@ static fir::Value* performAllocation(cgn::CodegenState* cs, sst::AllocOp* alloc, }, [cs, callUserCode, actuallyStore, alloc, ctrp, arrp]() { - auto ptr = cs->irb.PointerAdd(arrp, ctrp); + auto ptr = cs->irb.GetPointer(arrp, ctrp); actuallyStore(ptr); if(alloc->initBlock) callUserCode(cs->irb.Dereference(ptr), ctrp); - cs->irb.Store(cs->irb.Add(ctrp, fir::ConstantInt::getInt64(1)), ctrp); + cs->irb.Store(cs->irb.Add(ctrp, fir::ConstantInt::getNative(1)), ctrp); }); } } @@ -135,8 +133,8 @@ static fir::Value* performAllocation(cgn::CodegenState* cs, sst::AllocOp* alloc, if(counts.empty() || isRaw) { - fir::Value* cnt = (counts.empty() ? fir::ConstantInt::getInt64(1) - : cs->oneWayAutocast(counts[0]->codegen(cs, fir::Type::getInt64()).value, fir::Type::getInt64())); + fir::Value* cnt = (counts.empty() ? fir::ConstantInt::getNative(1) + : cs->oneWayAutocast(counts[0]->codegen(cs, fir::Type::getNativeWord()).value, fir::Type::getNativeWord())); //* if we don't have a count, then we just return a T* -- no arrays, nothing. @@ -156,9 +154,9 @@ static fir::Value* performAllocation(cgn::CodegenState* cs, sst::AllocOp* alloc, { auto ecount = counts[0]; - auto count = cs->oneWayAutocast(ecount->codegen(cs, fir::Type::getInt64()).value, fir::Type::getInt64()); + auto count = cs->oneWayAutocast(ecount->codegen(cs, fir::Type::getNativeWord()).value, fir::Type::getNativeWord()); if(!count || !count->getType()->isIntegerType()) - error(ecount, "Expected integer type for length, found '%s' instead", (count ? count->getType()->str() : "null")); + error(ecount, "expected integer type for length, found '%s' instead", (count ? count->getType()->str() : "null")); // make sure the length isn't negative auto checkf = getCheckNegativeLengthFunction(cs); @@ -196,7 +194,7 @@ CGResult sst::AllocOp::_codegen(cgn::CodegenState* cs, fir::Type* infer) defer(cs->popLoc()); if(this->counts.size() > 1) - error(this, "Multi-dimensional arrays are not supported yet."); + error(this, "multi-dimensional arrays are not supported yet."); return CGResult(performAllocation(cs, this, this->elmType, this->counts, this->isRaw)); } diff --git a/source/codegen/arithmetic.cpp b/source/codegen/arithmetic.cpp index ee05fe74..ae14b6b6 100644 --- a/source/codegen/arithmetic.cpp +++ b/source/codegen/arithmetic.cpp @@ -1,5 +1,5 @@ // arithmetic.cpp -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "sst.h" @@ -32,7 +32,7 @@ namespace sst if(!res) { - error(this, "Case type of '%s' is '%s', cannot cast to type '%s'", vt, vt->toEnumType()->getCaseType(), target); + error(this, "case type of '%s' is '%s', cannot cast to type '%s'", vt, vt->toEnumType()->getCaseType(), target); } return CGResult(res); @@ -56,7 +56,7 @@ namespace sst fir::IRBlock* invalid = cs->irb.addNewBlockInFunction("invalid", cs->irb.getCurrentFunction()); fir::IRBlock* merge = cs->irb.addNewBlockInFunction("merge", cs->irb.getCurrentFunction()); - auto targetId = fir::ConstantInt::getInt64(target->toUnionVariantType()->getVariantId()); + auto targetId = fir::ConstantInt::getNative(target->toUnionVariantType()->getVariantId()); auto variantId = cs->irb.GetUnionVariantID(value); auto valid = cs->irb.ICmpEQ(targetId, variantId); @@ -84,7 +84,7 @@ namespace sst if(!res) { - error(this, "No appropriate cast from type '%s' to '%s'", + error(this, "no appropriate cast from type '%s' to '%s'", vt, target); } @@ -100,22 +100,22 @@ namespace sst { // get the type out. auto res = cs->irb.BitwiseAND(cs->irb.GetAnyTypeID(value), - cs->irb.BitwiseNOT(fir::ConstantInt::getUint64(BUILTIN_ANY_FLAG_MASK))); + cs->irb.BitwiseNOT(fir::ConstantInt::getUNative(BUILTIN_ANY_FLAG_MASK))); - return CGResult(res = cs->irb.ICmpEQ(res, fir::ConstantInt::getUint64(target->getID()))); + return CGResult(res = cs->irb.ICmpEQ(res, fir::ConstantInt::getUNative(target->getID()))); } else if(value->getType()->isUnionType() && target->isUnionVariantType()) { // it's slightly more complicated. auto vid1 = cs->irb.GetUnionVariantID(value); - auto vid2 = fir::ConstantInt::getInt64(target->toUnionVariantType()->getVariantId()); + auto vid2 = fir::ConstantInt::getNative(target->toUnionVariantType()->getVariantId()); return CGResult(cs->irb.ICmpEQ(vid1, vid2)); } else { - auto res = fir::ConstantInt::getUint64(value->getType()->getID()); - return CGResult(cs->irb.ICmpEQ(res, fir::ConstantInt::getUint64(target->getID()))); + auto res = fir::ConstantInt::getUNative(value->getType()->getID()); + return CGResult(cs->irb.ICmpEQ(res, fir::ConstantInt::getUNative(target->getID()))); } } @@ -145,17 +145,17 @@ namespace sst if(lv->getType() != func->getArguments()[0]->getType()) { SpanError::make(SimpleError::make(this->left->loc, - "Mismatched types for left side of overloaded binary operator '%s'; expected '%s', found '%s' instead", + "mismatched types for left side of overloaded binary operator '%s'; expected '%s', found '%s' instead", this->op, func->getArguments()[0]->getType(), lv->getType()) - )->append(SimpleError::make(MsgType::Note, this->overloadedOpFunction->loc, "Operator was overloaded here:")) + )->append(SimpleError::make(MsgType::Note, this->overloadedOpFunction->loc, "operator was overloaded here:")) ->postAndQuit(); } else if(rv->getType() != func->getArguments()[1]->getType()) { SpanError::make(SimpleError::make(this->right->loc, - "Mismatched types for right side of overloaded binary operator '%s'; expected '%s', found '%s' instead", + "mismatched types for right side of overloaded binary operator '%s'; expected '%s', found '%s' instead", this->op, func->getArguments()[1]->getType(), rv->getType()) - )->append(SimpleError::make(MsgType::Note, this->overloadedOpFunction->loc, "Operator was overloaded here:")) + )->append(SimpleError::make(MsgType::Note, this->overloadedOpFunction->loc, "operator was overloaded here:")) ->postAndQuit(); } @@ -190,9 +190,9 @@ namespace sst if(val->getType() != func->getArguments()[0]->getType()) { - SpanError::make(SimpleError::make(this->expr->loc, "Mismatched types for overloaded unary operator '%s'; expected '%s', found '%s' instead", - this->op, func->getArguments()[0]->getType(), val->getType())) - ->append(SimpleError::make(MsgType::Note, this->overloadedOpFunction->loc, "Operator was overloaded here:")) + SpanError::make(SimpleError::make(this->expr->loc, "mismatched types for overloaded unary operator '%s'; " + "expected '%s', found '%s' instead", this->op, func->getArguments()[0]->getType(), val->getType())) + ->append(SimpleError::make(MsgType::Note, this->overloadedOpFunction->loc, "operator was overloaded here:")) ->postAndQuit(); } @@ -242,10 +242,10 @@ namespace sst else if(this->op == Operator::AddressOf) { if(!val->islorclvalue()) - error(this, "Cannot take address of a non-lvalue"); + error(this, "cannot take address of a non-lvalue"); else if(val->getType()->isFunctionType()) - error(this, "Cannot take the address of a function; use it as a value type"); + error(this, "cannot take the address of a function; use it as a value type"); return CGResult(cs->irb.AddressOf(val, false)); } @@ -275,7 +275,7 @@ namespace cgn { auto unsupportedError = [loc, op](const Location& al, fir::Type* a, const Location& bl, fir::Type* b) { - SpanError::make(SimpleError::make(loc, "Unsupported operator '%s' between types '%s' and '%s'", op, a, b)) + SpanError::make(SimpleError::make(loc, "unsupported operator '%s' between types '%s' and '%s'", op, a, b)) ->add(util::ESpan(al, strprintf("type '%s'", a))) ->add(util::ESpan(bl, strprintf("type '%s'", b))) ->postAndQuit(); @@ -355,7 +355,7 @@ namespace cgn auto cmpfn = cgn::glue::string::getCompareFunction(this); fir::Value* res = this->irb.Call(cmpfn, lv, rv); - fir::Value* zero = fir::ConstantInt::getInt64(0); + fir::Value* zero = fir::ConstantInt::getNative(0); if(op == Operator::CompareEQ) return CGResult(this->irb.ICmpEQ(res, zero)); if(op == Operator::CompareNEQ) return CGResult(this->irb.ICmpNEQ(res, zero)); @@ -386,7 +386,7 @@ namespace cgn auto cmpfn = cgn::glue::array::getCompareFunction(this, lt, 0); fir::Value* res = this->irb.Call(cmpfn, lv, rv); - fir::Value* zero = fir::ConstantInt::getInt64(0); + fir::Value* zero = fir::ConstantInt::getNative(0); if(op == Operator::CompareEQ) return CGResult(this->irb.ICmpEQ(res, zero)); if(op == Operator::CompareNEQ) return CGResult(this->irb.ICmpNEQ(res, zero)); @@ -399,7 +399,7 @@ namespace cgn } else { - error("Unsupported comparison between types '%s' and '%s'", lt, rt); + error("unsupported comparison between types '%s' and '%s'", lt, rt); } } else @@ -414,12 +414,12 @@ namespace cgn || ((lt->isIntegerType() || lt->isConstantNumberType()) && rt->isPointerType())) { auto ofsv = (lt->isPointerType() ? rv : lv); - auto ofs = this->oneWayAutocast(ofsv, fir::Type::getInt64()); + auto ofs = this->oneWayAutocast(ofsv, fir::Type::getNativeWord()); iceAssert(ofs->getType()->isIntegerType()); auto ptr = (lt->isPointerType() ? lv : rv); - ptr = this->irb.PointerAdd(ptr, ofs); + ptr = this->irb.GetPointer(ptr, ofs); return CGResult(ptr); } diff --git a/source/codegen/assign.cpp b/source/codegen/assign.cpp index 02f84882..c985e73f 100644 --- a/source/codegen/assign.cpp +++ b/source/codegen/assign.cpp @@ -1,5 +1,5 @@ // assign.cpp -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "sst.h" @@ -20,13 +20,13 @@ CGResult sst::AssignOp::_codegen(cgn::CodegenState* cs, fir::Type* infer) if(!lr->islorclvalue()) { - SpanError::make(SimpleError::make(this->loc, "Cannot assign to non-lvalue (most likely a temporary) expression")) + SpanError::make(SimpleError::make(this->loc, "cannot assign to non-lvalue (most likely a temporary) expression")) ->add(util::ESpan(this->left->loc, "here")) ->postAndQuit(); } else if(lr->isclvalue()) { - SpanError::make(SimpleError::make(this->loc, "Cannot assign to immutable expression")) + SpanError::make(SimpleError::make(this->loc, "cannot assign to immutable expression")) ->add(util::ESpan(this->left->loc, "here")) ->postAndQuit(); } @@ -51,7 +51,7 @@ CGResult sst::AssignOp::_codegen(cgn::CodegenState* cs, fir::Type* infer) { // right then. if(!lr->islvalue()) - error(this, "Cannot append to an r-value array"); + error(this, "cannot append to an r-value array"); auto appendf = cgn::glue::array::getAppendFunction(cs, lt->toDynamicArrayType()); @@ -65,7 +65,7 @@ CGResult sst::AssignOp::_codegen(cgn::CodegenState* cs, fir::Type* infer) { // right then. if(!lr->islvalue()) - error(this, "Cannot append to an r-value array"); + error(this, "cannot append to an r-value array"); auto appendf = cgn::glue::array::getElementAppendFunction(cs, lt->toDynamicArrayType()); @@ -79,7 +79,7 @@ CGResult sst::AssignOp::_codegen(cgn::CodegenState* cs, fir::Type* infer) { // right then. if(!lr->islvalue()) - error(this, "Cannot append to an r-value array"); + error(this, "cannot append to an r-value array"); auto appendf = cgn::glue::string::getAppendFunction(cs); @@ -93,7 +93,7 @@ CGResult sst::AssignOp::_codegen(cgn::CodegenState* cs, fir::Type* infer) { // right then. if(!lr->islvalue()) - error(this, "Cannot append to an r-value string"); + error(this, "cannot append to an r-value string"); auto appendf = cgn::glue::string::getCharAppendFunction(cs); @@ -117,12 +117,12 @@ CGResult sst::AssignOp::_codegen(cgn::CodegenState* cs, fir::Type* infer) if(rr == 0) { - error(this, "Invalid assignment from value of type '%s' to expected type '%s'", rr->getType(), lt); + error(this, "invalid assignment from value of type '%s' to expected type '%s'", rr->getType(), lt); } // ok then if(lt != rr->getType()) - error(this, "What? left = %s, right = %s", lt, rr->getType()); + error(this, "what? left = %s, right = %s", lt, rr->getType()); cs->autoAssignRefCountedValue(lr, rr, /* isInitial: */ false, /* performStore: */ true); return CGResult(0); @@ -139,7 +139,7 @@ CGResult sst::TupleAssignOp::_codegen(cgn::CodegenState* cs, fir::Type* infer) auto tuple = this->right->codegen(cs).value; if(!tuple->getType()->isTupleType()) - error(this->right, "Expected tuple type in assignment to tuple on left-hand-side; found type '%s' instead", tuple->getType()); + error(this->right, "expected tuple type in assignment to tuple on left-hand-side; found type '%s' instead", tuple->getType()); auto tty = tuple->getType()->toTupleType(); @@ -149,8 +149,8 @@ CGResult sst::TupleAssignOp::_codegen(cgn::CodegenState* cs, fir::Type* infer) for(auto v : this->lefts) { auto res = v->codegen(cs, tty->getElementN(idx)); - if(res->islvalue()) - error(v, "Cannot assign to non-lvalue expression in tuple assignment"); + if(!res->islvalue()) + error(v, "cannot assign to non-lvalue expression in tuple assignment"); results.push_back(res); idx++; @@ -164,7 +164,7 @@ CGResult sst::TupleAssignOp::_codegen(cgn::CodegenState* cs, fir::Type* infer) auto rr = cs->oneWayAutocast(val, lr.value->getType()); if(!rr || rr->getType() != lr.value->getType()) { - error(this->right, "Mismatched types in assignment to tuple element %d; assigning type '%s' to '%s'", + error(this->right, "mismatched types in assignment to tuple element %d; assigning type '%s' to '%s'", val->getType(), lr.value->getType()); } diff --git a/source/codegen/autocasting.cpp b/source/codegen/autocasting.cpp index e0a05707..2549fd9a 100644 --- a/source/codegen/autocasting.cpp +++ b/source/codegen/autocasting.cpp @@ -1,5 +1,5 @@ // autocasting.cpp -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "sst.h" @@ -29,11 +29,11 @@ namespace cgn } else { - if(ty->getMinBits() <= fir::Type::getInt64()->getBitWidth() - 1) - return fir::ConstantInt::getInt64(cn->getInt64()); + if(ty->getMinBits() < fir::Type::getNativeWord()->getBitWidth() - 1) + return fir::ConstantInt::getNative(cn->getInt64()); - else if(ty->isSigned() && ty->getMinBits() <= fir::Type::getUint64()->getBitWidth()) - return fir::ConstantInt::getUint64(cn->getUint64()); + else if(!ty->isSigned() && ty->getMinBits() <= fir::Type::getNativeUWord()->getBitWidth()) + return fir::ConstantInt::getUNative(cn->getUint64()); else error("int overflow"); @@ -44,7 +44,7 @@ namespace cgn static fir::ConstantValue* _unwrapConstantNumber(CodegenState* cs, fir::ConstantNumber* num, fir::Type* target, bool isAutocast) { if(!(target->isIntegerType() || target->isFloatingPointType())) - error(cs->loc(), "Unable to cast number literal to inferred type '%s'", target); + error(cs->loc(), "unable to cast number literal to inferred type '%s'", target); auto ty = num->getType()->toConstantNumberType(); @@ -52,12 +52,12 @@ namespace cgn if(ty->isFloating() && target->isIntegerType()) { if(isAutocast) return 0; - warn(cs->loc(), "Casting floating-point literal to integer type '%s' will cause a truncation", target); + warn(cs->loc(), "casting floating-point literal to integer type '%s' will cause a truncation", target); } else if(target->isIntegerType() && !target->isSignedIntType() && ty->isSigned()) { if(isAutocast) return 0; - warn(cs->loc(), "Casting negative literal to an unsigned integer type '%s'", target); + warn(cs->loc(), "casting negative literal to an unsigned integer type '%s'", target); signConvert = true; } @@ -65,7 +65,7 @@ namespace cgn if(target->toPrimitiveType()->getBitWidth() < ty->getMinBits()) { // TODO: actually do what we say. - warn(cs->loc(), "Casting literal to type '%s' will cause an overflow; value will be truncated bitwise to fit", + warn(cs->loc(), "casting literal to type '%s' will cause an overflow; value will be truncated bitwise to fit", target); } @@ -101,7 +101,10 @@ namespace cgn else if(target == fir::Type::getUint16()) return fir::ConstantInt::get(target, num->getUint16()); else if(target == fir::Type::getUint32()) return fir::ConstantInt::get(target, num->getUint32()); else if(target == fir::Type::getUint64()) return fir::ConstantInt::get(target, num->getUint64()); - else error("unsupported type '%s'", target); + + else if(target == fir::Type::getNativeWord()) return fir::ConstantInt::get(target, num->getInt64()); + else if(target == fir::Type::getNativeUWord()) return fir::ConstantInt::get(target, num->getUint64()); + else error("unsupported type '%s'", target); } @@ -153,7 +156,7 @@ namespace cgn } else if(fromType->isStringType() && target == fir::Type::getInt8Ptr()) { - result = this->irb.GetSAAData(from); + result = this->irb.PointerTypeCast(this->irb.GetSAAData(from), fir::Type::getInt8Ptr()); } else if(fromType->isStringType() && target->isCharSliceType()) { diff --git a/source/codegen/builtin.cpp b/source/codegen/builtin.cpp index cefae85c..119878f3 100644 --- a/source/codegen/builtin.cpp +++ b/source/codegen/builtin.cpp @@ -1,5 +1,5 @@ // builtin.cpp -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "errors.h" @@ -10,9 +10,9 @@ static fir::Value* checkNullPointerOrReturnZero(cgn::CodegenState* cs, fir::Value* ptr) { - iceAssert(ptr->getType() == fir::Type::getInt64Ptr()); + iceAssert(ptr->getType() == fir::Type::getNativeWordPtr()); - auto isnull = cs->irb.ICmpEQ(ptr, fir::ConstantValue::getZeroValue(fir::Type::getInt64Ptr())); + auto isnull = cs->irb.ICmpEQ(ptr, fir::ConstantValue::getZeroValue(fir::Type::getNativeWordPtr())); auto prevb = cs->irb.getCurrentBlock(); auto deref = cs->irb.addNewBlockAfter("deref", prevb); @@ -25,8 +25,8 @@ static fir::Value* checkNullPointerOrReturnZero(cgn::CodegenState* cs, fir::Valu cs->irb.UnCondBranch(merge); cs->irb.setCurrentBlock(merge); - auto phi = cs->irb.CreatePHINode(fir::Type::getInt64()); - phi->addIncoming(fir::ConstantInt::getInt64(0), prevb); + auto phi = cs->irb.CreatePHINode(fir::Type::getNativeWord()); + phi->addIncoming(fir::ConstantInt::getNative(0), prevb); phi->addIncoming(rc, deref); return phi; @@ -50,7 +50,7 @@ CGResult sst::BuiltinDotOp::_codegen(cgn::CodegenState* cs, fir::Type* infer) iceAssert(arguments.empty()); auto clonef = cgn::glue::saa_common::generateCloneFunction(cs, ty); - auto ret = cs->irb.Call(clonef, cs->irb.CreateSliceFromSAA(res.value, false), fir::ConstantInt::getInt64(0)); + auto ret = cs->irb.Call(clonef, cs->irb.CreateSliceFromSAA(res.value, false), fir::ConstantInt::getNative(0)); iceAssert(fir::isRefCountedType(ret->getType())); cs->addRefCountedValue(ret); @@ -62,10 +62,10 @@ CGResult sst::BuiltinDotOp::_codegen(cgn::CodegenState* cs, fir::Type* infer) iceAssert(!ty->isStringType()); if(!res->islvalue()) - error(this->lhs, "Cannot call 'pop()' on an rvalue"); + error(this->lhs, "cannot call 'pop()' on an rvalue"); else if(ty->isArrayType()) - error(this->lhs, "Cannot call 'pop()' on an array type ('%s')", ty); + error(this->lhs, "cannot call 'pop()' on an array type ('%s')", ty); auto popf = cgn::glue::array::getPopElementFromBackFunction(cs, ty); auto tupl = cs->irb.Call(popf, res.value, fir::ConstantString::get(this->loc.toString())); @@ -84,7 +84,7 @@ CGResult sst::BuiltinDotOp::_codegen(cgn::CodegenState* cs, fir::Type* infer) iceAssert(arguments.size() == 1); if(!res->islvalue()) - error(this->lhs, "Cannot call 'append' on an rvalue"); + error(this->lhs, "cannot call 'append' on an rvalue"); auto arg = arguments[0]; fir::Function* appendf = cgn::glue::saa_common::generateAppropriateAppendFunction(cs, ty, arg->getType()); @@ -141,7 +141,7 @@ CGResult sst::BuiltinDotOp::_codegen(cgn::CodegenState* cs, fir::Type* infer) { if(this->name == BUILTIN_SAA_FIELD_LENGTH) { - return CGResult(fir::ConstantInt::getInt64(ty->toArrayType()->getArraySize())); + return CGResult(fir::ConstantInt::getNative(ty->toArrayType()->getArraySize())); } else if(this->name == BUILTIN_SAA_FIELD_POINTER) { @@ -185,7 +185,7 @@ CGResult sst::BuiltinDotOp::_codegen(cgn::CodegenState* cs, fir::Type* infer) iceAssert(namearr->getType()->isPointerType() && namearr->getType()->getPointerElementType()->isArrayType()); auto idx = cs->irb.GetEnumCaseIndex(res.value); - auto n = cs->irb.GEP2(namearr, fir::ConstantInt::getInt64(0), idx); + auto n = cs->irb.GEP2(namearr, fir::ConstantInt::getNative(0), idx); return CGResult(cs->irb.ReadPtr(n)); } @@ -193,7 +193,7 @@ CGResult sst::BuiltinDotOp::_codegen(cgn::CodegenState* cs, fir::Type* infer) } - error(this, "No such property or builtin method '%s' on type '%s'", this->name, ty); + error(this, "no property or builtin method '%s' on type '%s'", this->name, ty); } diff --git a/source/codegen/call.cpp b/source/codegen/call.cpp index 9b96b4bf..17363d3a 100644 --- a/source/codegen/call.cpp +++ b/source/codegen/call.cpp @@ -1,8 +1,9 @@ // call.cpp -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "sst.h" +#include "mpool.h" #include "codegen.h" #include "gluecode.h" @@ -90,22 +91,22 @@ static std::vector _codegenAndArrangeFunctionCallArguments(cgn::Cod // cs syntax feels a little dirty. val = cs->oneWayAutocast(vr.value, inf); - vr.value = val; - if(val->getType() != inf) { auto errs = SpanError::make(SimpleError::make(arg.loc, - "Mismatched type in function call; parameter %d has type '%s', but given argument has type '%s'", i, inf, val->getType())); + "mismatched type in function call; parameter %d has type '%s', but given argument has type '%s'", i, inf, vr.value->getType())); if(ft->isVariadicFunc() && i >= numArgs - 1) { - errs->add(util::ESpan(arg.loc, strprintf("Argument's type '%s' cannot be cast to the expected variadic element type '%s'", - val->getType(), inf))); + errs->add(util::ESpan(arg.loc, strprintf("argument's type '%s' cannot be cast to the expected variadic element type '%s'", + vr.value->getType(), inf))); } errs->postAndQuit(); } + + vr.value = val; } else if(ft->isCStyleVarArg()) { @@ -125,6 +126,9 @@ static std::vector _codegenAndArrangeFunctionCallArguments(cgn::Cod // int32 can represent you even if you're unsigned else if(val->getType()->isIntegerType() && val->getType()->toPrimitiveType()->getIntegerBitWidth() < 32) val = cs->irb.IntSizeCast(val, val->getType()->isSignedIntType() ? fir::Type::getInt32() : fir::Type::getUint32()); + + else if(val->getType()->isBoolType()) + val = cs->irb.IntZeroExt(val, fir::Type::getInt32()); } ret[i] = val; @@ -137,7 +141,7 @@ static std::vector _codegenAndArrangeFunctionCallArguments(cgn::Cod { auto et = ft->getArgumentTypes().back()->getArrayElementType(); ret.push_back(fir::ConstantArraySlice::get(fir::ArraySliceType::getVariadic(et), - fir::ConstantValue::getZeroValue(et->getPointerTo()), fir::ConstantInt::getInt64(0))); + fir::ConstantValue::getZeroValue(et->getPointerTo()), fir::ConstantInt::getNative(0))); } return ret; @@ -162,7 +166,7 @@ CGResult sst::FunctionCall::_codegen(cgn::CodegenState* cs, fir::Type* infer) defer(cs->popLoc()); if(!this->target) - error(this, "Failed to find target for function call to '%s'", this->name); + error(this, "failed to find target for function call to '%s'", this->name); // check this target fir::Value* vf = 0; @@ -214,7 +218,7 @@ CGResult sst::FunctionCall::_codegen(cgn::CodegenState* cs, fir::Type* infer) ft = vt->getPointerElementType()->toFunctionType(); - warn(this, "Prefer using functions to function pointers"); + warn(this, "prefer using functions to function pointers"); } iceAssert(ft); @@ -222,7 +226,7 @@ CGResult sst::FunctionCall::_codegen(cgn::CodegenState* cs, fir::Type* infer) //! SELF HANDLING (INSERTION) (CODEGEN) if(auto fd = dcast(FunctionDefn, this->target); fd && fd->parentTypeForMethod && cs->isInMethodBody() && this->isImplicitMethodCall) { - auto fake = new RawValueExpr(this->loc, fd->parentTypeForMethod->getPointerTo()); + auto fake = util::pool(this->loc, fd->parentTypeForMethod->getPointerTo()); fake->rawValue = CGResult(cs->irb.AddressOf(cs->getMethodSelf(), true)); this->arguments.insert(this->arguments.begin(), FnCallArgument(this->loc, "self", fake, 0)); @@ -231,13 +235,13 @@ CGResult sst::FunctionCall::_codegen(cgn::CodegenState* cs, fir::Type* infer) size_t numArgs = ft->getArgumentTypes().size(); if(!ft->isCStyleVarArg() && !ft->isVariadicFunc() && this->arguments.size() != numArgs) { - error(this, "Mismatch in number of arguments in call to '%s'; %zu %s provided, but %zu %s expected", + error(this, "mismatch in number of arguments in call to '%s'; %zu %s provided, but %zu %s expected", this->name, this->arguments.size(), this->arguments.size() == 1 ? "was" : "were", numArgs, numArgs == 1 ? "was" : "were"); } else if((ft->isCStyleVarArg() || !ft->isVariadicFunc()) && this->arguments.size() < numArgs) { - error(this, "Need at least %zu arguments to call variadic function '%s', only have %zu", + error(this, "need at least %zu arguments to call variadic function '%s', only have %zu", numArgs, this->name, this->arguments.size()); } @@ -284,7 +288,7 @@ static CGResult callBuiltinTypeConstructor(cgn::CodegenState* cs, fir::Type* typ auto ret = cs->oneWayAutocast(args[0]->codegen(cs, type).value, type); if(type != ret->getType()) - error(args[0], "Mismatched type in builtin type initialiser; expected '%s', found '%s'", type, ret->getType()); + error(args[0], "mismatched type in builtin type initialiser; expected '%s', found '%s'", type, ret->getType()); return CGResult(ret); } @@ -297,7 +301,7 @@ static CGResult callBuiltinTypeConstructor(cgn::CodegenState* cs, fir::Type* typ auto clonef = cgn::glue::string::getCloneFunction(cs); iceAssert(clonef); - auto ret = cs->irb.Call(clonef, slc, fir::ConstantInt::getInt64(0)); + auto ret = cs->irb.Call(clonef, slc, fir::ConstantInt::getNative(0)); cs->addRefCountedValue(ret); return CGResult(ret); @@ -315,7 +319,7 @@ static CGResult callBuiltinTypeConstructor(cgn::CodegenState* cs, fir::Type* typ iceAssert(args[1]->type->isIntegerType()); auto ptr = args[0]->codegen(cs).value; - auto len = cs->oneWayAutocast(args[1]->codegen(cs, fir::Type::getInt64()).value, fir::Type::getInt64()); + auto len = cs->oneWayAutocast(args[1]->codegen(cs, fir::Type::getNativeWord()).value, fir::Type::getNativeWord()); auto slc = cs->irb.CreateValue(fir::Type::getCharSlice(false)); slc = cs->irb.SetArraySliceData(slc, (ptr->getType()->isMutablePointer() ? cs->irb.PointerTypeCast(ptr, fir::Type::getInt8Ptr()) : ptr)); @@ -347,7 +351,7 @@ CGResult sst::ExprCall::_codegen(cgn::CodegenState* cs, fir::Type* infer) { if((!ft->isVariadicFunc() && !ft->isCStyleVarArg()) || this->arguments.size() < ft->getArgumentTypes().size()) { - error(this, "Mismatched number of arguments; expected %zu, but %zu were given", + error(this, "mismatched number of arguments; expected %zu, but %zu were given", ft->getArgumentTypes().size(), this->arguments.size()); } } diff --git a/source/codegen/classes.cpp b/source/codegen/classes.cpp index 0ad940bc..a8096755 100644 --- a/source/codegen/classes.cpp +++ b/source/codegen/classes.cpp @@ -1,10 +1,11 @@ // classes.h -// Copyright (c) 2017, zhiayang@gmail.com +// Copyright (c) 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "errors.h" #include "codegen.h" #include "typecheck.h" +#include "mpool.h" CGResult sst::ClassDefn::_codegen(cgn::CodegenState* cs, fir::Type* infer) { @@ -129,7 +130,7 @@ fir::Value* cgn::CodegenState::callVirtualMethod(sst::FunctionCall* call) { iceAssert(this->isInMethodBody() && fd->parentTypeForMethod); - auto fake = new sst::RawValueExpr(call->loc, fd->parentTypeForMethod->getPointerTo()); + auto fake = util::pool(call->loc, fd->parentTypeForMethod->getPointerTo()); fake->rawValue = CGResult(this->getMethodSelf()); call->arguments.insert(call->arguments.begin(), FnCallArgument(call->loc, "self", fake, 0)); diff --git a/source/codegen/codegenstate.cpp b/source/codegen/codegenstate.cpp index 1956013a..478cb34f 100644 --- a/source/codegen/codegenstate.cpp +++ b/source/codegen/codegenstate.cpp @@ -1,10 +1,11 @@ // codegenstate.cpp -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "errors.h" #include "codegen.h" #include "platform.h" +#include "gluecode.h" #include "typecheck.h" namespace cgn @@ -70,7 +71,7 @@ namespace cgn void CodegenState::leaveFunction() { if(this->functionStack.empty()) - error(this->loc(), "Not a in function"); + error(this->loc(), "not a in function"); this->functionStack.pop_back(); } @@ -78,7 +79,7 @@ namespace cgn fir::Function* CodegenState::getCurrentFunction() { if(this->functionStack.empty()) - error(this->loc(), "Not a in function"); + error(this->loc(), "not a in function"); return this->functionStack.back(); } @@ -127,11 +128,14 @@ namespace cgn - + void CodegenState::pushLoc(const Location& l) + { + this->locationStack.push_back(l); + } void CodegenState::pushLoc(sst::Stmt* stmt) { - this->locationStack.push_back(stmt->loc); + this->pushLoc(stmt->loc); } void CodegenState::popLoc() @@ -180,9 +184,9 @@ namespace cgn arr = this->irb.SetSAAData(arr, this->irb.PointerTypeCast(this->irb.GetArraySliceData(fir::ConstantString::get("")), fir::Type::getMutInt8Ptr())); - arr = this->irb.SetSAALength(arr, fir::ConstantInt::getInt64(0)); - arr = this->irb.SetSAACapacity(arr, fir::ConstantInt::getInt64(0)); - arr = this->irb.SetSAARefCountPointer(arr, fir::ConstantValue::getZeroValue(fir::Type::getInt64Ptr())); + arr = this->irb.SetSAALength(arr, fir::ConstantInt::getNative(0)); + arr = this->irb.SetSAACapacity(arr, fir::ConstantInt::getNative(0)); + arr = this->irb.SetSAARefCountPointer(arr, fir::ConstantValue::getZeroValue(fir::Type::getNativeWord()->getPointerTo())); ret = arr; } @@ -191,9 +195,9 @@ namespace cgn fir::Value* arr = this->irb.CreateValue(type); arr = this->irb.SetSAAData(arr, fir::ConstantValue::getZeroValue(type->getArrayElementType()->getMutablePointerTo())); - arr = this->irb.SetSAALength(arr, fir::ConstantInt::getInt64(0)); - arr = this->irb.SetSAACapacity(arr, fir::ConstantInt::getInt64(0)); - arr = this->irb.SetSAARefCountPointer(arr, fir::ConstantValue::getZeroValue(fir::Type::getInt64Ptr())); + arr = this->irb.SetSAALength(arr, fir::ConstantInt::getNative(0)); + arr = this->irb.SetSAACapacity(arr, fir::ConstantInt::getNative(0)); + arr = this->irb.SetSAARefCountPointer(arr, fir::ConstantValue::getZeroValue(fir::Type::getNativeWord()->getPointerTo())); ret = arr; } @@ -201,7 +205,7 @@ namespace cgn { fir::Value* arr = this->irb.CreateValue(type); arr = this->irb.SetArraySliceData(arr, fir::ConstantValue::getZeroValue(type->getArrayElementType()->getPointerTo())); - arr = this->irb.SetArraySliceLength(arr, fir::ConstantInt::getInt64(0)); + arr = this->irb.SetArraySliceLength(arr, fir::ConstantInt::getNative(0)); ret = arr; } @@ -228,8 +232,8 @@ namespace cgn if(ifn == 0) { - SimpleError::make(this->loc(), "Class '%s' cannot be automatically initialised as it does not have a constructor taking 0 arguments", - cls->getTypeName())->append(SimpleError::make(MsgType::Note, clsdef->loc, "Class '%s' was defined here:", clsdef->id.name)) + SimpleError::make(this->loc(), "class '%s' cannot be automatically initialised as it does not have a constructor taking 0 arguments", + cls->getTypeName())->append(SimpleError::make(MsgType::Note, clsdef->loc, "class '%s' was defined here:", clsdef->id.name)) ->postAndQuit(); } @@ -275,7 +279,7 @@ namespace cgn if(name == ALLOCATE_MEMORY_FUNC) { return this->module->getOrCreateFunction(Identifier(ALLOCATE_MEMORY_FUNC, IdKind::Name), - fir::FunctionType::get({ fir::Type::getInt64() }, fir::Type::getMutInt8Ptr()), fir::LinkageType::External); + fir::FunctionType::get({ fir::Type::getNativeWord() }, fir::Type::getMutInt8Ptr()), fir::LinkageType::External); } else if(name == FREE_MEMORY_FUNC) { @@ -285,7 +289,8 @@ namespace cgn else if(name == REALLOCATE_MEMORY_FUNC) { return this->module->getOrCreateFunction(Identifier(REALLOCATE_MEMORY_FUNC, IdKind::Name), - fir::FunctionType::get({ fir::Type::getMutInt8Ptr(), fir::Type::getInt64() }, fir::Type::getMutInt8Ptr()), fir::LinkageType::External); + fir::FunctionType::get({ fir::Type::getMutInt8Ptr(), fir::Type::getNativeWord() }, fir::Type::getMutInt8Ptr()), + fir::LinkageType::External); } else if(name == CRT_FDOPEN) { @@ -310,7 +315,7 @@ namespace cgn else if(name == "strlen") { return this->module->getOrCreateFunction(Identifier("strlen", IdKind::Name), - fir::FunctionType::get({ fir::Type::getInt8Ptr() }, fir::Type::getInt64()), fir::LinkageType::External); + fir::FunctionType::get({ fir::Type::getInt8Ptr() }, fir::Type::getNativeWord()), fir::LinkageType::External); } else if(name == "fprintf") { @@ -338,7 +343,7 @@ namespace cgn } else if(storage->getType()->getPointerElementType() != value->getType()) { - error(this->loc(), "Cannot store value of type '%s' into storage of type '%s'", value->getType(), + error(this->loc(), "cannot store value of type '%s' into storage of type '%s'", value->getType(), storage->getType()); } @@ -353,7 +358,7 @@ namespace cgn if(!this->globalInitFunc) { - fir::Function* func = this->module->getOrCreateFunction(Identifier("__global_init_function__", IdKind::Name), + fir::Function* func = this->module->getOrCreateFunction(util::obfuscateIdentifier(BUILTIN_GLOBAL_INIT_FUNCTION_NAME), fir::FunctionType::get({ }, fir::Type::getVoid()), fir::LinkageType::Internal); fir::IRBlock* entry = this->irb.addNewBlockInFunction("entry", func); diff --git a/source/codegen/constructor.cpp b/source/codegen/constructor.cpp index af424e8a..a3d79545 100644 --- a/source/codegen/constructor.cpp +++ b/source/codegen/constructor.cpp @@ -1,9 +1,10 @@ // constructor.cpp -// Copyright (c) 2017, zhiayang@gmail.com +// Copyright (c) 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "sst.h" #include "codegen.h" +#include "mpool.h" fir::Value* cgn::CodegenState::getConstructedStructValue(fir::StructType* str, const std::vector& args) { @@ -62,7 +63,7 @@ void cgn::CodegenState::constructClassWithArguments(fir::ClassType* cls, sst::Fu // make a copy auto arguments = args; { - auto fake = new sst::RawValueExpr(this->loc(), cls->getPointerTo()); + auto fake = util::pool(this->loc(), cls->getPointerTo()); fake->rawValue = CGResult(selfptr); arguments.insert(arguments.begin(), FnCallArgument(this->loc(), "self", fake, 0)); @@ -71,9 +72,9 @@ void cgn::CodegenState::constructClassWithArguments(fir::ClassType* cls, sst::Fu if(arguments.size() != constrfn->getArgumentCount()) { - SimpleError::make(this->loc(), "Mismatched number of arguments in constructor call to class '%s'; expected %d, found %d instead", + SimpleError::make(this->loc(), "mismatched number of arguments in constructor call to class '%s'; expected %d, found %d instead", (fir::Type*) cls, constrfn->getArgumentCount(), arguments.size()) - ->append(SimpleError::make(MsgType::Note, constr->loc, "Constructor was defined here:")) + ->append(SimpleError::make(MsgType::Note, constr->loc, "constructor was defined here:")) ->postAndQuit(); } @@ -95,14 +96,14 @@ CGResult sst::StructConstructorCall::_codegen(cgn::CodegenState* cs, fir::Type* this->target->codegen(cs); if(!this->target) - error(this, "Failed to find target type of constructor call"); + error(this, "failed to find target type of constructor call"); //* note: we don't need an explicit thing telling us whether we should use names or not //* if the first argument has no name, then we're not using names; if it has a name, then we are //* and ofc expect consistency, but we should have already typechecked that previously. StructDefn* str = dcast(StructDefn, this->target); - if(!str) error(this, "Non-struct type '%s' not supported in constructor call", this->target->id.name); + if(!str) error(this, "non-struct type '%s' not supported in constructor call", this->target->id.name); // great. now we just make the thing. fir::Value* value = cs->getConstructedStructValue(str->type->toStructType(), this->arguments); diff --git a/source/codegen/controlflow.cpp b/source/codegen/controlflow.cpp index 30941d00..d660b690 100644 --- a/source/codegen/controlflow.cpp +++ b/source/codegen/controlflow.cpp @@ -1,5 +1,5 @@ // controlflow.cpp -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "sst.h" @@ -43,7 +43,7 @@ CGResult sst::IfStmt::_codegen(cgn::CodegenState* cs, fir::Type* infer) iceAssert(firstCond); if(!firstCond->getType()->isBoolType()) - error(this->cases.front().cond, "Non-boolean type '%s' cannot be used as a conditional", firstCond->getType()); + error(this->cases.front().cond, "non-boolean type '%s' cannot be used as a conditional", firstCond->getType()); // do a comparison @@ -86,7 +86,7 @@ CGResult sst::IfStmt::_codegen(cgn::CodegenState* cs, fir::Type* infer) iceAssert(cond); if(!cond->getType()->isBoolType()) - error(elif.cond, "Non-boolean type '%s' cannot be used as a conditional", cond->getType()); + error(elif.cond, "non-boolean type '%s' cannot be used as a conditional", cond->getType()); // ok auto trueblk = cs->irb.addNewBlockAfter("trueCase-" + elif.body->loc.shortString(), cs->irb.getCurrentBlock()); diff --git a/source/codegen/destructure.cpp b/source/codegen/destructure.cpp index 0b84d2a7..15cf65fb 100644 --- a/source/codegen/destructure.cpp +++ b/source/codegen/destructure.cpp @@ -1,11 +1,11 @@ // destructure.cpp -// Copyright (c) 2017, zhiayang@gmail.com +// Copyright (c) 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "sst.h" #include "codegen.h" #include "gluecode.h" - +#include "mpool.h" CGResult sst::DecompDefn::_codegen(cgn::CodegenState* cs, fir::Type* infer) @@ -40,7 +40,7 @@ static void handleDefn(cgn::CodegenState* cs, sst::VarDefn* defn, CGResult res) if(defn) { - auto v = new sst::RawValueExpr(defn->loc, res.value->getType()); + auto v = util::pool(defn->loc, res.value->getType()); v->rawValue = res; defn->init = v; @@ -84,18 +84,19 @@ static void checkArray(cgn::CodegenState* cs, const DecompMapping& bind, CGResul bool shouldSliceBeMutable = sst::getMutabilityOfSliceOfType(rt); if(!rt->isArrayType() && !rt->isDynamicArrayType() && !rt->isArraySliceType() && !rt->isStringType()) - error(bind.loc, "Expected array type in destructuring declaration; found type '%s' instead", rt); + error(bind.loc, "expected array type in destructuring declaration; found type '%s' instead", rt); if(rt->isStringType()) { // do a bounds check. - auto numbinds = fir::ConstantInt::getInt64(bind.inner.size()); + auto numbinds = fir::ConstantInt::getNative(bind.inner.size()); { auto checkf = cgn::glue::string::getBoundsCheckFunction(cs, true); - iceAssert(checkf); - - auto strloc = fir::ConstantString::get(bind.loc.toString()); - cs->irb.Call(checkf, cs->irb.GetSAALength(rhs.value), numbinds, strloc); + if(checkf) + { + auto strloc = fir::ConstantString::get(bind.loc.toString()); + cs->irb.Call(checkf, cs->irb.GetSAALength(rhs.value), numbinds, strloc); + } } //* note: special-case this, because 1. we want to return chars @@ -104,7 +105,7 @@ static void checkArray(cgn::CodegenState* cs, const DecompMapping& bind, CGResul size_t idx = 0; for(auto& b : bind.inner) { - auto v = CGResult(cs->irb.ReadPtr(cs->irb.PointerAdd(strdat, fir::ConstantInt::getInt64(idx)))); + auto v = CGResult(cs->irb.ReadPtr(cs->irb.GetPointer(strdat, fir::ConstantInt::getNative(idx)))); cs->generateDecompositionBindings(b, v, false); idx++; @@ -119,7 +120,7 @@ static void checkArray(cgn::CodegenState* cs, const DecompMapping& bind, CGResul auto remaining = cs->irb.Subtract(cs->irb.GetSAALength(rhs.value), numbinds); auto slice = cs->irb.CreateValue(fir::Type::getCharSlice(shouldSliceBeMutable)); - slice = cs->irb.SetArraySliceData(slice, cs->irb.PointerAdd(strdat, numbinds)); + slice = cs->irb.SetArraySliceData(slice, cs->irb.GetPointer(strdat, numbinds)); slice = cs->irb.SetArraySliceLength(slice, remaining); handleDefn(cs, bind.restDefn, CGResult(slice)); @@ -143,19 +144,20 @@ static void checkArray(cgn::CodegenState* cs, const DecompMapping& bind, CGResul auto array = rhs.value; fir::Value* arrlen = 0; - auto numbinds = fir::ConstantInt::getInt64(bind.inner.size()); + auto numbinds = fir::ConstantInt::getNative(bind.inner.size()); { - //* note: 'true' means we're performing a decomposition, so print a more appropriate error message on bounds failure. - auto checkf = cgn::glue::array::getBoundsCheckFunction(cs, true); - iceAssert(checkf); - - if(rt->isArrayType()) arrlen = fir::ConstantInt::getInt64(rt->toArrayType()->getArraySize()); + if(rt->isArrayType()) arrlen = fir::ConstantInt::getNative(rt->toArrayType()->getArraySize()); else if(rt->isArraySliceType()) arrlen = cs->irb.GetArraySliceLength(array); else if(rt->isDynamicArrayType()) arrlen = cs->irb.GetSAALength(array); else iceAssert(0); - auto strloc = fir::ConstantString::get(bind.loc.toString()); - cs->irb.Call(checkf, arrlen, numbinds, strloc); + //* note: 'true' means we're performing a decomposition, so print a more appropriate error message on bounds failure. + auto checkf = cgn::glue::array::getBoundsCheckFunction(cs, true); + if(checkf) + { + auto strloc = fir::ConstantString::get(bind.loc.toString()); + cs->irb.Call(checkf, arrlen, numbinds, strloc); + } } // # if 0 @@ -177,9 +179,9 @@ static void checkArray(cgn::CodegenState* cs, const DecompMapping& bind, CGResul idx++; } - warn(bind.loc, "Destructure of array without pointer (shouldn't happen!)"); + warn(bind.loc, "destructure of array without pointer (shouldn't happen!)"); if(!bind.restName.empty()) - error(bind.loc, "Could not get pointer to array (of type '%s') to create binding for '...'", rt); + error(bind.loc, "could not get pointer to array (of type '%s') to create binding for '...'", rt); } else // #endif @@ -196,7 +198,7 @@ static void checkArray(cgn::CodegenState* cs, const DecompMapping& bind, CGResul size_t idx = 0; for(auto& b : bind.inner) { - auto ptr = cs->irb.PointerAdd(data, fir::ConstantInt::getInt64(idx)); + auto ptr = cs->irb.GetPointer(data, fir::ConstantInt::getNative(idx)); auto v = CGResult(cs->irb.Dereference(ptr)); cs->generateDecompositionBindings(b, v, true); @@ -213,7 +215,7 @@ static void checkArray(cgn::CodegenState* cs, const DecompMapping& bind, CGResul auto remaining = cs->irb.Subtract(arrlen, numbinds); auto slice = cs->irb.CreateValue(sty); - slice = cs->irb.SetArraySliceData(slice, cs->irb.PointerAdd(data, numbinds)); + slice = cs->irb.SetArraySliceData(slice, cs->irb.GetPointer(data, numbinds)); slice = cs->irb.SetArraySliceLength(slice, remaining); handleDefn(cs, bind.restDefn, CGResult(slice)); @@ -229,7 +231,7 @@ static void checkArray(cgn::CodegenState* cs, const DecompMapping& bind, CGResul { clonee = cs->irb.CreateValue(fir::ArraySliceType::get(rt->getArrayElementType(), shouldSliceBeMutable)); clonee = cs->irb.SetArraySliceData(clonee, data); - clonee = cs->irb.SetArraySliceLength(clonee, fir::ConstantInt::getInt64(rt->toArrayType()->getArraySize())); + clonee = cs->irb.SetArraySliceLength(clonee, fir::ConstantInt::getNative(rt->toArrayType()->getArraySize())); } else { @@ -255,7 +257,7 @@ void cgn::CodegenState::generateDecompositionBindings(const DecompMapping& bind, if(!bind.name.empty()) { if(bind.ref && !allowref) - error(bind.loc, "Cannot bind to value of type '%s' by reference", rt); + error(bind.loc, "cannot bind to value of type '%s' by reference", rt); if(bind.ref) { diff --git a/source/codegen/directives.cpp b/source/codegen/directives.cpp new file mode 100644 index 00000000..bb2382be --- /dev/null +++ b/source/codegen/directives.cpp @@ -0,0 +1,118 @@ +// directives.cpp +// Copyright (c) 2019, zhiayang +// Licensed under the Apache License Version 2.0. + +#include "sst.h" +#include "codegen.h" +#include "platform.h" + +#include "ir/interp.h" + + +fir::ConstantValue* magicallyRunExpressionAtCompileTime(cgn::CodegenState* cs, sst::Stmt* stmt, fir::Type* infer, const Identifier& fname) +{ + // what we do is to make a new function in IR, set the insertpoint to that, + // then run codegen on the expression (so it generates inside), restore the insertpoint, + // then run the interpreter on that function (after compiling it), then get the interp::Value + // result, make a constantvalue with it + + + auto restore = cs->irb.getCurrentBlock(); + + bool isExpr = false; + fir::Type* retty = 0; + if(auto ex = dcast(sst::Expr, stmt); ex) isExpr = true, retty = ex->type; + else retty = fir::Type::getVoid(); + + auto fn = cs->module->getOrCreateFunction(fname, fir::FunctionType::get({ }, retty), fir::LinkageType::Internal); + iceAssert(fn); + + // make the function: + { + auto entry = cs->irb.addNewBlockInFunction("entry", fn); + cs->irb.setCurrentBlock(entry); + + fir::Value* ret = 0; + + if(isExpr) ret = stmt->codegen(cs, infer).value; + else stmt->codegen(cs, infer); + + if(!ret || ret->getType()->isVoidType()) + cs->irb.ReturnVoid(); + + else + cs->irb.Return(ret); + + if(restore) cs->irb.setCurrentBlock(restore); + } + + // run the function: + fir::ConstantValue* ret = 0; + { + auto is = new fir::interp::InterpState(cs->module); + is->initialise(); + { + auto result = is->runFunction(is->compileFunction(fn), { }); + + if(!retty->isVoidType()) + ret = is->unwrapInterpValueIntoConstant(result); + } + is->finalise(); + + delete is; + } + + return ret; +} + + + + + + + +static size_t runDirectiveId = 0; +CGResult sst::RunDirective::_codegen(cgn::CodegenState* cs, fir::Type* infer) +{ + cs->pushLoc(this); + defer(cs->popLoc()); + + sst::Stmt* toExec = 0; + if(this->insideExpr) toExec = this->insideExpr; + else toExec = this->block; + + auto ret = magicallyRunExpressionAtCompileTime(cs, toExec, infer, util::obfuscateIdentifier("run_directive", runDirectiveId++)); + return CGResult(ret); +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/source/codegen/dotop.cpp b/source/codegen/dotop.cpp index d4422e1b..ccdec2ba 100644 --- a/source/codegen/dotop.cpp +++ b/source/codegen/dotop.cpp @@ -1,11 +1,11 @@ // dotops.cpp -// Copyright (c) 2017, zhiayang@gmail.com +// Copyright (c) 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "errors.h" #include "codegen.h" #include "typecheck.h" - +#include "mpool.h" static bool isAutoDereferencable(fir::Type* t) { @@ -41,7 +41,7 @@ static CGResult getAppropriateValuePointer(cgn::CodegenState* cs, sst::Expr* use } else { - error(user, "Invalid type '%s' for instance dot op", restype); + error(user, "invalid type '%s' for instance dot op", restype); } return CGResult(retv); @@ -63,7 +63,7 @@ CGResult sst::MethodDotOp::_codegen(cgn::CodegenState* cs, fir::Type* infer) auto res = getAppropriateValuePointer(cs, this, this->lhs, &sty); // then we insert it as the first argument - auto rv = new sst::RawValueExpr(this->loc, res.value->getType()->getMutablePointerTo()); + auto rv = util::pool(this->loc, res.value->getType()->getMutablePointerTo()); rv->rawValue = CGResult(cs->irb.AddressOf(res.value, true)); fc->arguments.insert(fc->arguments.begin(), FnCallArgument(this->loc, "self", rv, 0)); @@ -204,7 +204,7 @@ CGResult cgn::CodegenState::getStructFieldImplicitly(std::string name) } else { - error(this->loc(), "Type '%s' has no field named '%s'", sty->getTypeName().str(), name); + error(this->loc(), "type '%s' has no field named '%s'", sty->getTypeName().str(), name); } }; @@ -215,5 +215,5 @@ CGResult cgn::CodegenState::getStructFieldImplicitly(std::string name) return dothing(ty->toClassType()); else - error(this->loc(), "Invalid self type '%s' for field named '%s'", ty, name); + error(this->loc(), "invalid self type '%s' for field named '%s'", ty, name); } diff --git a/source/codegen/enums.cpp b/source/codegen/enums.cpp index fc0c9ebe..28e3441e 100644 --- a/source/codegen/enums.cpp +++ b/source/codegen/enums.cpp @@ -1,5 +1,5 @@ // enums.cpp -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "sst.h" @@ -67,18 +67,18 @@ CGResult sst::EnumCaseDefn::_codegen(cgn::CodegenState* cs, fir::Type* infer) iceAssert(v); if(dcast(fir::ConstantValue, v) == 0) - error(this, "Enumeration case value ('%s' of type '%s') must be constant", this->id.name, v->getType()); + error(this, "enumeration case value ('%s' of type '%s') must be constant", this->id.name, v->getType()); } else { - v = fir::ConstantInt::getInt64(this->index); + v = fir::ConstantInt::getNative(this->index); } this->value = dcast(fir::ConstantValue, v); { auto ty = this->parentEnum->type; - auto ret = fir::ConstantEnumCase::get(ty->toEnumType(), fir::ConstantInt::getInt64(this->index), this->value); + auto ret = fir::ConstantEnumCase::get(ty->toEnumType(), fir::ConstantInt::getNative(this->index), this->value); cs->valueMap[this] = CGResult(ret); } @@ -90,24 +90,6 @@ CGResult sst::EnumCaseDefn::_codegen(cgn::CodegenState* cs, fir::Type* infer) CGResult sst::EnumDotOp::_codegen(cgn::CodegenState* cs, fir::Type* infer) { error("unsupported"); - // cs->pushLoc(this); - // defer(cs->popLoc()); - - // auto enr = this->enumeration; - // iceAssert(enr); - - // auto ecd = enr->cases[this->caseName]; - // iceAssert(ecd); - - // // ok, return the thing - // auto ty = enr->type; - // // info(this, "type = %s", ty); - - // auto ret = cs->irb.CreateValue(ty); - // ret = cs->irb.SetEnumCaseIndex(ret, fir::ConstantInt::getInt64(ecd->index)); - // ret = cs->irb.SetEnumCaseValue(ret, ecd->value); - - // return CGResult(ret); } diff --git a/source/codegen/function.cpp b/source/codegen/function.cpp index 84da8765..0a3623da 100644 --- a/source/codegen/function.cpp +++ b/source/codegen/function.cpp @@ -1,9 +1,10 @@ // function.cpp -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "sst.h" #include "codegen.h" +#include "mpool.h" #include "ir/irbuilder.h" @@ -87,8 +88,8 @@ CGResult sst::FunctionDefn::_codegen(cgn::CodegenState* cs, fir::Type* infer) { if(cs->entryFunction.first != 0) { - SimpleError::make(this->loc, "Redefinition of entry function with '@entry'") - ->append(SimpleError::make(MsgType::Note, cs->entryFunction.second, "Previous entry function marked here:")) + SimpleError::make(this->loc, "redefinition of entry function with '@entry'") + ->append(SimpleError::make(MsgType::Note, cs->entryFunction.second, "previous entry function marked here:")) ->postAndQuit(); } @@ -121,12 +122,15 @@ CGResult sst::ForeignFuncDefn::_codegen(cgn::CodegenState* cs, fir::Type* infer) auto ef = cs->module->getFunction(realId); if(ef && ef->getType() != ft) { - error(this, "Foreign function '%s' already defined elsewhere (with signature %s); overloading not possible", + error(this, "foreign function '%s' already defined elsewhere (with signature %s); overloading not possible", this->id.str(), ef->getType()); } auto fn = cs->module->getOrCreateFunction(realId, ft, fir::LinkageType::External); + if(this->isIntrinsic) + fn->setIsIntrinsic(); + cs->valueMap[this] = CGResult(fn); return CGResult(fn); } diff --git a/source/codegen/glue/any.cpp b/source/codegen/glue/any.cpp index 0c12f71a..0ac45896 100644 --- a/source/codegen/glue/any.cpp +++ b/source/codegen/glue/any.cpp @@ -1,5 +1,5 @@ // any.cpp -// Copyright (c) 2017, zhiayang@gmail.com +// Copyright (c) 2017, zhiayang // Licensed under the Apache License Version 2.0. @@ -19,13 +19,13 @@ namespace any fir::IRBlock* merge = cs->irb.addNewBlockInFunction("merge", func); fir::IRBlock* dorc = cs->irb.addNewBlockInFunction("dorc", func); - cs->irb.CondBranch(cs->irb.ICmpEQ(rcp, fir::ConstantValue::getZeroValue(fir::Type::getInt64Ptr())), + cs->irb.CondBranch(cs->irb.ICmpEQ(rcp, fir::ConstantValue::getZeroValue(fir::Type::getNativeWordPtr())), merge, dorc); cs->irb.setCurrentBlock(dorc); { auto oldrc = cs->irb.ReadPtr(rcp, "oldrc"); - auto newrc = cs->irb.Add(oldrc, fir::ConstantInt::getInt64(decrement ? -1 : 1)); + auto newrc = cs->irb.Add(oldrc, fir::ConstantInt::getNative(decrement ? -1 : 1)); cs->irb.SetAnyRefCount(any, newrc); @@ -42,12 +42,12 @@ namespace any { fir::IRBlock* dofree = cs->irb.addNewBlockInFunction("dofree", func); - auto cond = cs->irb.ICmpEQ(newrc, fir::ConstantInt::getInt64(0)); + auto cond = cs->irb.ICmpEQ(newrc, fir::ConstantInt::getNative(0)); // this thing checks for the MSB of the typeid; if it's set, means we used heap memory and so we need to free. cond = cs->irb.BitwiseAND(cond, - cs->irb.ICmpGT(cs->irb.BitwiseAND(cs->irb.GetAnyTypeID(any), fir::ConstantInt::getUint64(BUILTIN_ANY_FLAG_MASK)), - fir::ConstantInt::getUint64(0))); + cs->irb.ICmpGT(cs->irb.BitwiseAND(cs->irb.GetAnyTypeID(any), fir::ConstantInt::getUNative(BUILTIN_ANY_FLAG_MASK)), + fir::ConstantInt::getUNative(0))); cs->irb.CondBranch(cond, dofree, merge); @@ -68,7 +68,7 @@ namespace any // 2. 'buf' is a pointer to the array itself -- we cast it to i64*, so the dereference // gives us the first 8 bytes of the data buffer. - buf = cs->irb.PointerTypeCast(buf, fir::Type::getInt64Ptr()); + buf = cs->irb.PointerTypeCast(buf, fir::Type::getNativeWordPtr()); // 3. this is the dereference. auto ptr = cs->irb.ReadPtr(buf); @@ -102,14 +102,14 @@ namespace any fir::Function* getRefCountIncrementFunction(CodegenState* cs) { - auto fname = "__incr_rc_" + fir::Type::getAny()->str(); - fir::Function* retfn = cs->module->getFunction(Identifier(fname, IdKind::Name)); + auto fname = misc::getIncrRefcount_FName(fir::Type::getAny()); + fir::Function* retfn = cs->module->getFunction(fname); if(!retfn) { auto restore = cs->irb.getCurrentBlock(); - fir::Function* func = cs->module->getOrCreateFunction(Identifier(fname, IdKind::Name), + fir::Function* func = cs->module->getOrCreateFunction(fname, fir::FunctionType::get({ fir::Type::getAny() }, fir::Type::getVoid()), fir::LinkageType::Internal); func->setAlwaysInline(); @@ -129,14 +129,14 @@ namespace any fir::Function* getRefCountDecrementFunction(CodegenState* cs) { - auto fname = "__decr_rc_" + fir::Type::getAny()->str(); - fir::Function* retfn = cs->module->getFunction(Identifier(fname, IdKind::Name)); + auto fname = misc::getDecrRefcount_FName(fir::Type::getAny()); + fir::Function* retfn = cs->module->getFunction(fname); if(!retfn) { auto restore = cs->irb.getCurrentBlock(); - fir::Function* func = cs->module->getOrCreateFunction(Identifier(fname, IdKind::Name), + fir::Function* func = cs->module->getOrCreateFunction(fname, fir::FunctionType::get({ fir::Type::getAny() }, fir::Type::getVoid()), fir::LinkageType::Internal); func->setAlwaysInline(); @@ -158,14 +158,14 @@ namespace any fir::Function* generateCreateAnyWithValueFunction(CodegenState* cs, fir::Type* type) { - auto fname = "__create_any_of_" + type->str(); - fir::Function* retfn = cs->module->getFunction(Identifier(fname, IdKind::Name)); + auto fname = misc::getCreateAnyOf_FName(type); + fir::Function* retfn = cs->module->getFunction(fname); if(!retfn) { auto restore = cs->irb.getCurrentBlock(); - fir::Function* func = cs->module->getOrCreateFunction(Identifier(fname, IdKind::Name), + fir::Function* func = cs->module->getOrCreateFunction(fname, fir::FunctionType::get({ type }, fir::Type::getAny()), fir::LinkageType::Internal); func->setAlwaysInline(); @@ -179,10 +179,10 @@ namespace any // make the refcount pointer. auto rcp = cs->irb.PointerTypeCast(cs->irb.Call(cs->getOrDeclareLibCFunction(ALLOCATE_MEMORY_FUNC), - fir::ConstantInt::getInt64(REFCOUNT_SIZE)), fir::Type::getInt64Ptr()); + fir::ConstantInt::getNative(REFCOUNT_SIZE)), fir::Type::getNativeWordPtr()); any = cs->irb.SetAnyRefCountPointer(any, rcp); - cs->irb.SetAnyRefCount(any, fir::ConstantInt::getInt64(1)); + cs->irb.SetAnyRefCount(any, fir::ConstantInt::getNative(1)); size_t tid = type->getID(); if(auto typesz = fir::getSizeOfType(type); typesz > BUILTIN_ANY_DATA_BYTECOUNT) @@ -190,21 +190,21 @@ namespace any tid |= BUILTIN_ANY_FLAG_MASK; auto ptr = cs->irb.PointerTypeCast(cs->irb.Call(cs->getOrDeclareLibCFunction(ALLOCATE_MEMORY_FUNC), - fir::ConstantInt::getInt64(typesz)), type->getMutablePointerTo()); + fir::ConstantInt::getNative(typesz)), type->getMutablePointerTo()); #if DEBUG_ANY_ALLOCATION { cs->printIRDebugMessage("* ANY: alloc(): (id: %lu, ptr: %p / rcp: %p)", { - fir::ConstantInt::getUint64(tid), ptr, rcp }); + fir::ConstantInt::getUNative(tid), ptr, rcp }); } #endif cs->irb.WritePtr(func->getArguments()[0], ptr); - ptr = cs->irb.PointerToIntCast(ptr, fir::Type::getInt64()); + ptr = cs->irb.PointerToIntCast(ptr, fir::Type::getNativeWord()); // now, we make a fake data, and then store it. auto arrptr = cs->irb.StackAlloc(dataarrty); - auto fakeptr = cs->irb.PointerTypeCast(arrptr, fir::Type::getMutInt64Ptr()); + auto fakeptr = cs->irb.PointerTypeCast(arrptr, fir::Type::getNativeWordPtr()->getMutablePointerVersion()); cs->irb.WritePtr(ptr, fakeptr); auto arr = cs->irb.ReadPtr(arrptr); @@ -220,7 +220,7 @@ namespace any any = cs->irb.SetAnyData(any, arr); } - any = cs->irb.SetAnyTypeID(any, fir::ConstantInt::getUint64(tid)); + any = cs->irb.SetAnyTypeID(any, fir::ConstantInt::getUNative(tid)); cs->irb.Return(any); cs->irb.setCurrentBlock(restore); @@ -233,14 +233,14 @@ namespace any fir::Function* generateGetValueFromAnyFunction(CodegenState* cs, fir::Type* type) { - auto fname = strprintf("__get_value_of_%s_from_any", type->str()); - fir::Function* retfn = cs->module->getFunction(Identifier(fname, IdKind::Name)); + auto fname = misc::getGetValueFromAny_FName(type); + fir::Function* retfn = cs->module->getFunction(fname); if(!retfn) { auto restore = cs->irb.getCurrentBlock(); - fir::Function* func = cs->module->getOrCreateFunction(Identifier(fname, IdKind::Name), + fir::Function* func = cs->module->getOrCreateFunction(fname, fir::FunctionType::get({ fir::Type::getAny() }, type), fir::LinkageType::Internal); func->setAlwaysInline(); @@ -251,19 +251,19 @@ namespace any auto any = func->getArguments()[0]; auto dataarrty = fir::ArrayType::get(fir::Type::getInt8(), BUILTIN_ANY_DATA_BYTECOUNT); - auto tid = cs->irb.BitwiseAND(cs->irb.GetAnyTypeID(any), fir::ConstantInt::getUint64(~BUILTIN_ANY_FLAG_MASK)); + auto tid = cs->irb.BitwiseAND(cs->irb.GetAnyTypeID(any), fir::ConstantInt::getUNative(~BUILTIN_ANY_FLAG_MASK)); fir::IRBlock* invalid = cs->irb.addNewBlockInFunction("invalid", cs->irb.getCurrentFunction()); fir::IRBlock* merge = cs->irb.addNewBlockInFunction("merge", cs->irb.getCurrentFunction()); - auto valid = cs->irb.ICmpEQ(tid, fir::ConstantInt::getUint64(type->getID())); + auto valid = cs->irb.ICmpEQ(tid, fir::ConstantInt::getUNative(type->getID())); cs->irb.CondBranch(valid, merge, invalid); cs->irb.setCurrentBlock(invalid); { printRuntimeError(cs, fir::ConstantString::get(cs->loc().toString()), "invalid unwrap of 'any' with type id '%ld' into type '%s' (with id '%ld')", - { tid, cs->module->createGlobalString(type->str()), fir::ConstantInt::getUint64(type->getID()) } + { tid, cs->module->createGlobalString(type->str()), fir::ConstantInt::getUNative(type->getID()) } ); } diff --git a/source/codegen/glue/arrays.cpp b/source/codegen/glue/arrays.cpp index 7468fcee..c2713526 100644 --- a/source/codegen/glue/arrays.cpp +++ b/source/codegen/glue/arrays.cpp @@ -1,38 +1,11 @@ // arrays.cpp -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "codegen.h" #include "platform.h" #include "gluecode.h" -#define BUILTIN_ARRAY_BOUNDS_CHECK_FUNC_NAME "__array_boundscheck" -#define BUILTIN_ARRAY_DECOMP_BOUNDS_CHECK_FUNC_NAME "__array_boundscheckdecomp" - -#define BUILTIN_ARRAY_CMP_FUNC_NAME "__array_compare" - -#define BUILTIN_ARRAY_SET_ELEMENTS_DEFAULT_NAME "__array_setelementsdefault" -#define BUILTIN_ARRAY_SET_ELEMENTS_NAME "__array_setelements" -#define BUILTIN_ARRAY_CALL_CLASS_CONSTRUCTOR "__array_callclassinit" - -#define BUILTIN_DYNARRAY_CLONE_FUNC_NAME "__darray_clone" -#define BUILTIN_DYNARRAY_APPEND_FUNC_NAME "__darray_append" -#define BUILTIN_DYNARRAY_APPEND_ELEMENT_FUNC_NAME "__darray_appendelement" -#define BUILTIN_DYNARRAY_POP_BACK_FUNC_NAME "__darray_popback" -#define BUILTIN_DYNARRAY_MAKE_FROM_TWO_FUNC_NAME "__darray_combinetwo" - -#define BUILTIN_DYNARRAY_RESERVE_ENOUGH_NAME "__darray_reservesufficient" -#define BUILTIN_DYNARRAY_RESERVE_EXTRA_NAME "__darray_reserveextra" - -#define BUITLIN_DYNARRAY_RECURSIVE_REFCOUNT_NAME "__darray_recursiverefcount" - -#define BUILTIN_SLICE_CLONE_FUNC_NAME "__slice_clone" -#define BUILTIN_SLICE_APPEND_FUNC_NAME "__slice_append" -#define BUILTIN_SLICE_APPEND_ELEMENT_FUNC_NAME "__slice_appendelement" - -#define BUILTIN_LOOP_INCR_REFCOUNT_FUNC_NAME "__loop_incr_refcount" -#define BUILTIN_LOOP_DECR_REFCOUNT_FUNC_NAME "__loop_decr_refcount" - namespace cgn { namespace glue { @@ -77,15 +50,15 @@ namespace array { iceAssert(cls); - auto name = BUILTIN_ARRAY_CALL_CLASS_CONSTRUCTOR + std::string("_") + cls->encodedStr(); - fir::Function* fn = cs->module->getFunction(Identifier(name, IdKind::Name)); + auto fname = misc::getCallClassConstructor_FName(cls); + fir::Function* fn = cs->module->getFunction(fname); if(!fn) { auto restore = cs->irb.getCurrentBlock(); - fir::Function* func = cs->module->getOrCreateFunction(Identifier(name, IdKind::Name), - fir::FunctionType::get({ cls->getPointerTo(), fir::Type::getInt64() }, fir::Type::getVoid()), fir::LinkageType::Internal); + fir::Function* func = cs->module->getOrCreateFunction(fname, + fir::FunctionType::get({ cls->getPointerTo(), fir::Type::getNativeWord() }, fir::Type::getVoid()), fir::LinkageType::Internal); func->setAlwaysInline(); @@ -102,10 +75,9 @@ namespace array fir::IRBlock* body = cs->irb.addNewBlockInFunction("body", func); fir::IRBlock* merge = cs->irb.addNewBlockInFunction("merge", func); - auto ctrptr = cs->irb.StackAlloc(fir::Type::getInt64()); + auto ctrptr = cs->irb.StackAlloc(fir::Type::getNativeWord()); // already set to 0 internally - // cs->irb.WritePtr(fir::ConstantInt::getInt64(0), ctrptr); cs->irb.UnCondBranch(check); cs->irb.setCurrentBlock(check); @@ -117,11 +89,11 @@ namespace array cs->irb.setCurrentBlock(body); { auto ctr = cs->irb.ReadPtr(ctrptr); - auto ptr = cs->irb.PointerAdd(arrdata, ctr); + auto ptr = cs->irb.GetPointer(arrdata, ctr); cs->constructClassWithArguments(cls, constr, ptr, args, true); - cs->irb.WritePtr(cs->irb.Add(ctr, fir::ConstantInt::getInt64(1)), ctrptr); + cs->irb.WritePtr(cs->irb.Add(ctr, fir::ConstantInt::getNative(1)), ctrptr); cs->irb.UnCondBranch(check); } @@ -144,15 +116,16 @@ namespace array { iceAssert(elmType); - auto name = BUILTIN_ARRAY_SET_ELEMENTS_NAME + std::string("_") + elmType->encodedStr(); - fir::Function* fn = cs->module->getFunction(Identifier(name, IdKind::Name)); + auto fname = misc::getSetElements_FName(elmType); + fir::Function* fn = cs->module->getFunction(fname); if(!fn) { auto restore = cs->irb.getCurrentBlock(); - fir::Function* func = cs->module->getOrCreateFunction(Identifier(name, IdKind::Name), - fir::FunctionType::get({ elmType->getMutablePointerTo(), fir::Type::getInt64(), elmType }, fir::Type::getVoid()), fir::LinkageType::Internal); + fir::Function* func = cs->module->getOrCreateFunction(fname, + fir::FunctionType::get({ elmType->getMutablePointerTo(), fir::Type::getNativeWord(), elmType }, fir::Type::getVoid()), + fir::LinkageType::Internal); func->setAlwaysInline(); @@ -168,10 +141,7 @@ namespace array fir::IRBlock* body = cs->irb.addNewBlockInFunction("body", func); fir::IRBlock* merge = cs->irb.addNewBlockInFunction("merge", func); - auto ctrptr = cs->irb.StackAlloc(fir::Type::getInt64()); - - // already set to 0 internally - // cs->irb.WritePtr(fir::ConstantInt::getInt64(0), ctrptr); + auto ctrptr = cs->irb.StackAlloc(fir::Type::getNativeWord()); cs->irb.UnCondBranch(check); cs->irb.setCurrentBlock(check); @@ -183,11 +153,11 @@ namespace array cs->irb.setCurrentBlock(body); { auto ctr = cs->irb.ReadPtr(ctrptr); - auto ptr = cs->irb.PointerAdd(arrdata, ctr); + auto ptr = cs->irb.GetPointer(arrdata, ctr); cs->autoAssignRefCountedValue(ptr, value, true, true); - cs->irb.WritePtr(cs->irb.Add(ctr, fir::ConstantInt::getInt64(1)), ctrptr); + cs->irb.WritePtr(cs->irb.Add(ctr, fir::ConstantInt::getNative(1)), ctrptr); cs->irb.UnCondBranch(check); } @@ -208,15 +178,15 @@ namespace array { iceAssert(elmType); - auto name = BUILTIN_ARRAY_SET_ELEMENTS_DEFAULT_NAME + std::string("_") + elmType->encodedStr(); - fir::Function* fn = cs->module->getFunction(Identifier(name, IdKind::Name)); + auto fname = misc::getSetElementsDefault_FName(elmType); + fir::Function* fn = cs->module->getFunction(fname); if(!fn) { auto restore = cs->irb.getCurrentBlock(); - fir::Function* func = cs->module->getOrCreateFunction(Identifier(name, IdKind::Name), - fir::FunctionType::get({ elmType->getMutablePointerTo(), fir::Type::getInt64() }, fir::Type::getVoid()), fir::LinkageType::Internal); + fir::Function* func = cs->module->getOrCreateFunction(fname, + fir::FunctionType::get({ elmType->getMutablePointerTo(), fir::Type::getNativeWord() }, fir::Type::getVoid()), fir::LinkageType::Internal); func->setAlwaysInline(); @@ -259,8 +229,8 @@ namespace array fir::Value* arg1, fir::Value* arg2) { // ok, ez. - fir::Value* zeroval = fir::ConstantInt::getInt64(0); - fir::Value* oneval = fir::ConstantInt::getInt64(1); + fir::Value* zeroval = fir::ConstantInt::getNative(0); + fir::Value* oneval = fir::ConstantInt::getNative(1); fir::IRBlock* cond = cs->irb.addNewBlockInFunction("cond", func); fir::IRBlock* body = cs->irb.addNewBlockInFunction("body", func); @@ -303,8 +273,8 @@ namespace array } else if(arrtype->isArrayType()) { - len1 = fir::ConstantInt::getInt64(arrtype->toArrayType()->getArraySize()); - len2 = fir::ConstantInt::getInt64(arrtype->toArrayType()->getArraySize()); + len1 = fir::ConstantInt::getNative(arrtype->toArrayType()->getArraySize()); + len2 = fir::ConstantInt::getNative(arrtype->toArrayType()->getArraySize()); } else { @@ -312,10 +282,10 @@ namespace array } // we compare to this to break - fir::Value* counter = cs->irb.StackAlloc(fir::Type::getInt64()); + fir::Value* counter = cs->irb.StackAlloc(fir::Type::getNativeWord()); cs->irb.WritePtr(zeroval, counter); - fir::Value* res = cs->irb.StackAlloc(fir::Type::getInt64()); + fir::Value* res = cs->irb.StackAlloc(fir::Type::getNativeWord()); cs->irb.WritePtr(zeroval, res); @@ -359,20 +329,20 @@ namespace array cs->irb.setCurrentBlock(retlt); - cs->irb.Return(fir::ConstantInt::getInt64(-1)); + cs->irb.Return(fir::ConstantInt::getNative(-1)); cs->irb.setCurrentBlock(reteq); - cs->irb.Return(fir::ConstantInt::getInt64(0)); + cs->irb.Return(fir::ConstantInt::getNative(0)); cs->irb.setCurrentBlock(retgt); - cs->irb.Return(fir::ConstantInt::getInt64(+1)); + cs->irb.Return(fir::ConstantInt::getNative(+1)); } cs->irb.setCurrentBlock(body); { - fir::Value* v1 = cs->irb.ReadPtr(cs->irb.PointerAdd(ptr1, cs->irb.ReadPtr(counter))); - fir::Value* v2 = cs->irb.ReadPtr(cs->irb.PointerAdd(ptr2, cs->irb.ReadPtr(counter))); + fir::Value* v1 = cs->irb.ReadPtr(cs->irb.GetPointer(ptr1, cs->irb.ReadPtr(counter))); + fir::Value* v2 = cs->irb.ReadPtr(cs->irb.GetPointer(ptr2, cs->irb.ReadPtr(counter))); fir::Value* c = cs->performBinaryOperation(cs->loc(), { cs->loc(), v1 }, { cs->loc(), v2 }, "==").value; @@ -381,7 +351,7 @@ namespace array // if c == true, then lhs == rhs, and so we should have 0. c = cs->irb.LogicalNot(c); - c = cs->irb.IntSizeCast(c, fir::Type::getInt64()); + c = cs->irb.IntSizeCast(c, fir::Type::getNativeWord()); cs->irb.WritePtr(c, res); @@ -412,7 +382,6 @@ namespace array static void _compareFunctionUsingOperatorFunction(CodegenState* cs, fir::Type* arrtype, fir::Function* curfunc, fir::Value* arg1, fir::Value* arg2, fir::Function* opf) { - // fir::Value* zeroval = fir::ConstantInt::getInt64(0); error("notsup"); } @@ -422,15 +391,15 @@ namespace array { iceAssert(arrtype); - auto name = BUILTIN_ARRAY_CMP_FUNC_NAME + std::string("_") + arrtype->encodedStr(); - fir::Function* cmpf = cs->module->getFunction(Identifier(name, IdKind::Name)); + auto fname = misc::getCompare_FName(arrtype); + fir::Function* cmpf = cs->module->getFunction(fname); if(!cmpf) { auto restore = cs->irb.getCurrentBlock(); - fir::Function* func = cs->module->getOrCreateFunction(Identifier(name, IdKind::Name), - fir::FunctionType::get({ arrtype, arrtype }, fir::Type::getInt64()), fir::LinkageType::Internal); + fir::Function* func = cs->module->getOrCreateFunction(fname, + fir::FunctionType::get({ arrtype, arrtype }, fir::Type::getNativeWord()), fir::LinkageType::Internal); func->setAlwaysInline(); @@ -472,14 +441,14 @@ namespace array static fir::Function* makeRecursiveRefCountingFunction(CodegenState* cs, fir::DynamicArrayType* arrtype, bool incr) { - auto name = BUITLIN_DYNARRAY_RECURSIVE_REFCOUNT_NAME + std::string(incr ? "_incr_" : "_decr_") + arrtype->encodedStr(); - fir::Function* retf = cs->module->getFunction(Identifier(name, IdKind::Name)); + auto fname = misc::getRecursiveRefcount_FName(arrtype, incr); + fir::Function* retf = cs->module->getFunction(fname); if(!retf) { auto restore = cs->irb.getCurrentBlock(); - fir::Function* func = cs->module->getOrCreateFunction(Identifier(name, IdKind::Name), + fir::Function* func = cs->module->getOrCreateFunction(fname, fir::FunctionType::get({ arrtype }, fir::Type::getVoid()), fir::LinkageType::Internal); func->setAlwaysInline(); @@ -504,7 +473,7 @@ namespace array auto dontrc = cs->irb.addNewBlockInFunction("dontrcliteral", cs->irb.getCurrentFunction()); { auto rcp = cs->irb.GetSAARefCountPointer(arr); - auto cond = cs->irb.ICmpNEQ(cs->irb.PointerToIntCast(rcp, fir::Type::getInt64()), fir::ConstantInt::getInt64(0)); + auto cond = cs->irb.ICmpNEQ(cs->irb.PointerToIntCast(rcp, fir::Type::getNativeWord()), fir::ConstantInt::getNative(0)); cs->irb.CondBranch(cond, dorc, dontrc); } @@ -515,8 +484,8 @@ namespace array therefc = cs->irb.GetSAARefCount(arr); fir::Value* newrc = 0; - if(incr) newrc = cs->irb.Add(therefc, fir::ConstantInt::getInt64(1)); - else newrc = cs->irb.Subtract(therefc, fir::ConstantInt::getInt64(1)); + if(incr) newrc = cs->irb.Add(therefc, fir::ConstantInt::getNative(1)); + else newrc = cs->irb.Subtract(therefc, fir::ConstantInt::getNative(1)); // update it. therefc = newrc; @@ -541,14 +510,14 @@ namespace array fir::IRBlock* dealloc = cs->irb.addNewBlockInFunction("dealloc", func); fir::IRBlock* merge = cs->irb.addNewBlockInFunction("merge", func); - auto zv = fir::ConstantInt::getInt64(0); + auto zv = fir::ConstantInt::getNative(0); //! NOTE: what we want to happen here is for us to free the memory, but only if refcnt == 0 && capacity >= 0 //* so our condition is (REFCOUNT == 0) & (CAP >= 0) - auto refc = cs->irb.CreatePHINode(fir::Type::getInt64()); + auto refc = cs->irb.CreatePHINode(fir::Type::getNativeWord()); refc->addIncoming(therefc, dorc); - refc->addIncoming(fir::ConstantInt::getInt64(-1), prevblk); + refc->addIncoming(fir::ConstantInt::getNative(-1), prevblk); auto dofree = cs->irb.BitwiseAND(cs->irb.ICmpEQ(refc, zv), cs->irb.ICmpGEQ(cap, zv)); @@ -565,7 +534,7 @@ namespace array // only when we free, do we loop through our array and decrement its refcount. if(fir::isRefCountedType(elmtype)) { - auto ctrp = cs->irb.StackAlloc(fir::Type::getInt64()); + auto ctrp = cs->irb.StackAlloc(fir::Type::getNativeWord()); cs->irb.WritePtr(zv, ctrp); cs->createWhileLoop([cs, ctrp, len](auto pass, auto fail) { @@ -575,11 +544,11 @@ namespace array [cs, ctrp, ptr]() { auto ctr = cs->irb.ReadPtr(ctrp); - auto p = cs->irb.PointerAdd(ptr, ctr); + auto p = cs->irb.GetPointer(ptr, ctr); cs->decrementRefCount(cs->irb.ReadPtr(p)); - cs->irb.WritePtr(cs->irb.Add(ctr, fir::ConstantInt::getInt64(1)), ctrp); + cs->irb.WritePtr(cs->irb.Add(ctr, fir::ConstantInt::getNative(1)), ctrp); }); } @@ -620,16 +589,14 @@ namespace array static fir::Function* _getDoRefCountFunctionForDynamicArray(CodegenState* cs, fir::DynamicArrayType* arrtype, bool increment) { - auto name = (increment ? BUILTIN_LOOP_INCR_REFCOUNT_FUNC_NAME : BUILTIN_LOOP_DECR_REFCOUNT_FUNC_NAME) - + std::string("_") + arrtype->encodedStr(); - - fir::Function* cmpf = cs->module->getFunction(Identifier(name, IdKind::Name)); + auto fname = (increment ? misc::getLoopIncrRefcount_FName(arrtype) : misc::getLoopDecrRefcount_FName(arrtype)); + fir::Function* cmpf = cs->module->getFunction(fname); if(!cmpf) { auto restore = cs->irb.getCurrentBlock(); - fir::Function* func = cs->module->getOrCreateFunction(Identifier(name, IdKind::Name), + fir::Function* func = cs->module->getOrCreateFunction(fname, fir::FunctionType::get({ arrtype }, arrtype), fir::LinkageType::Internal); func->setAlwaysInline(); @@ -693,8 +660,8 @@ namespace array iceAssert(arrtype); iceAssert(arrtype->isDynamicArrayType() || arrtype->isArraySliceType()); - auto name = BUILTIN_DYNARRAY_POP_BACK_FUNC_NAME + std::string("_") + arrtype->encodedStr(); - fir::Function* fn = cs->module->getFunction(Identifier(name, IdKind::Name)); + auto fname = misc::getPopBack_FName(arrtype); + fir::Function* fn = cs->module->getFunction(fname); if(!fn) { @@ -703,7 +670,7 @@ namespace array auto restore = cs->irb.getCurrentBlock(); auto retTy = fir::TupleType::get({ arrtype, arrtype->getArrayElementType() }); - fir::Function* func = cs->module->getOrCreateFunction(Identifier(name, IdKind::Name), + fir::Function* func = cs->module->getOrCreateFunction(fname, fir::FunctionType::get({ arrtype, fir::Type::getCharSlice(false) }, retTy), fir::LinkageType::Internal); func->setAlwaysInline(); @@ -720,25 +687,25 @@ namespace array fir::IRBlock* fail = cs->irb.addNewBlockInFunction("fail", func); fir::IRBlock* merge = cs->irb.addNewBlockInFunction("merge", func); - auto cond = cs->irb.ICmpLT(origlen, fir::ConstantInt::getInt64(1)); + auto cond = cs->irb.ICmpLT(origlen, fir::ConstantInt::getNative(1)); cs->irb.CondBranch(cond, fail, merge); cs->irb.setCurrentBlock(fail); { - printRuntimeError(cs, loc, "Calling pop() on an empty array\n", { }); + printRuntimeError(cs, loc, "calling pop() on an empty array\n", { }); } cs->irb.setCurrentBlock(merge); { - auto newlen = cs->irb.Subtract(origlen, fir::ConstantInt::getInt64(1)); + auto newlen = cs->irb.Subtract(origlen, fir::ConstantInt::getNative(1)); fir::Value* ret = 0; // first, load the last value if(isslice) { auto ptr = cs->irb.GetArraySliceData(arr); - auto val = cs->irb.ReadPtr(cs->irb.PointerAdd(ptr, newlen)); + auto val = cs->irb.ReadPtr(cs->irb.GetPointer(ptr, newlen)); auto newarr = cs->irb.SetArraySliceLength(arr, newlen); ret = cs->irb.CreateValue(retTy); @@ -748,7 +715,7 @@ namespace array else { auto ptr = cs->irb.GetSAAData(arr); - auto val = cs->irb.ReadPtr(cs->irb.PointerAdd(ptr, newlen)); + auto val = cs->irb.ReadPtr(cs->irb.GetPointer(ptr, newlen)); auto newarr = cs->irb.SetSAALength(arr, newlen); ret = cs->irb.CreateValue(retTy); diff --git a/source/codegen/glue/misc.cpp b/source/codegen/glue/misc.cpp index 490e85d9..f228ec88 100644 --- a/source/codegen/glue/misc.cpp +++ b/source/codegen/glue/misc.cpp @@ -1,14 +1,11 @@ // misc.cpp -// Copyright (c) 2017, zhiayang@gmail.com +// Copyright (c) 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "codegen.h" #include "platform.h" #include "gluecode.h" - -// generate runtime glue code -#define BUILTIN_MALLOC_WRAPPER_FUNC_NAME "__malloc_wrapper" -#define BUILTIN_RANGE_SANITY_CHECK_FUNC_NAME "__range_sanitycheck" +#include "frontend.h" namespace cgn { @@ -19,30 +16,32 @@ void printRuntimeError(cgn::CodegenState* cs, fir::Value* pos, std::string messa //! on windows, apparently fprintf doesn't like to work. //! so we just use normal printf. - iceAssert(pos->getType()->isCharSliceType()); - - #ifdef _WIN32 + if(!frontend::getIsNoRuntimeErrorStrings()) { - fir::Value* fmtstr = cs->module->createGlobalString(("\nRuntime error at %s:\n" + message + "\n").c_str()); - fir::Value* posstr = cs->irb.GetArraySliceData(pos); + iceAssert(pos->getType()->isCharSliceType()); - std::vector as = { fmtstr, posstr }; - as.insert(as.end(), args.begin(), args.end()); + #ifdef _WIN32 + { + fir::Value* fmtstr = cs->module->createGlobalString(("\nRuntime error at %s:\n" + message + "\n").c_str()); + fir::Value* posstr = cs->irb.GetArraySliceData(pos); - cs->irb.Call(cs->getOrDeclareLibCFunction("printf"), as); - } - #else - { - fir::Value* fmtstr = cs->module->createGlobalString(("\nRuntime error at %s:\n" + message + "\n").c_str()); - fir::Value* posstr = cs->irb.GetArraySliceData(pos); + std::vector as = { fmtstr, posstr }; + as.insert(as.end(), args.begin(), args.end()); - std::vector as = { fmtstr, posstr }; - as.insert(as.end(), args.begin(), args.end()); + cs->irb.Call(cs->getOrDeclareLibCFunction("printf"), as); + } + #else + { + fir::Value* fmtstr = cs->module->createGlobalString(("\nRuntime error at %s:\n" + message + "\n").c_str()); + fir::Value* posstr = cs->irb.GetArraySliceData(pos); - cs->irb.Call(cs->getOrDeclareLibCFunction("printf"), as); - } - #endif + std::vector as = { fmtstr, posstr }; + as.insert(as.end(), args.begin(), args.end()); + cs->irb.Call(cs->getOrDeclareLibCFunction("printf"), as); + } + #endif + } // cs->irb.Call(cs->getOrDeclareLibCFunction("exit"), fir::ConstantInt::getInt32(1)); cs->irb.Call(cs->getOrDeclareLibCFunction("abort")); @@ -54,14 +53,16 @@ namespace misc { fir::Function* getMallocWrapperFunction(CodegenState* cs) { - fir::Function* fn = cs->module->getFunction(Identifier(BUILTIN_MALLOC_WRAPPER_FUNC_NAME, IdKind::Name)); + auto fname = getMallocWrapper_FName(); + fir::Function* fn = cs->module->getFunction(fname); if(!fn) { auto restore = cs->irb.getCurrentBlock(); - fir::Function* func = cs->module->getOrCreateFunction(Identifier(BUILTIN_MALLOC_WRAPPER_FUNC_NAME, IdKind::Name), - fir::FunctionType::get({ fir::Type::getInt64(), fir::Type::getCharSlice(false) }, fir::Type::getMutInt8Ptr()), fir::LinkageType::Internal); + fir::Function* func = cs->module->getOrCreateFunction(fname, + fir::FunctionType::get({ fir::Type::getNativeWord(), fir::Type::getCharSlice(false) }, fir::Type::getMutInt8Ptr()), + fir::LinkageType::Internal); func->setAlwaysInline(); @@ -104,13 +105,17 @@ namespace misc fir::Function* getRangeSanityCheckFunction(CodegenState* cs) { - fir::Function* fn = cs->module->getFunction(Identifier(BUILTIN_RANGE_SANITY_CHECK_FUNC_NAME, IdKind::Name)); + if(frontend::getIsNoRuntimeChecks()) + return 0; + + auto fname = getRangeSanityCheck_FName(); + fir::Function* fn = cs->module->getFunction(fname); if(!fn) { auto restore = cs->irb.getCurrentBlock(); - fir::Function* func = cs->module->getOrCreateFunction(Identifier(BUILTIN_RANGE_SANITY_CHECK_FUNC_NAME, IdKind::Name), + fir::Function* func = cs->module->getOrCreateFunction(fname, fir::FunctionType::get({ fir::Type::getRange(), fir::Type::getCharSlice(false) }, fir::Type::getVoid()), fir::LinkageType::Internal); func->setAlwaysInline(); @@ -140,7 +145,7 @@ namespace misc auto upper = cs->irb.GetRangeUpper(func->getArguments()[0]); auto step = cs->irb.GetRangeStep(func->getArguments()[0]); - auto zero = fir::ConstantInt::getInt64(0); + auto zero = fir::ConstantInt::getNative(0); // first of all check if step is zero. { auto cond = cs->irb.ICmpEQ(step, zero); @@ -174,17 +179,17 @@ namespace misc { cs->irb.setCurrentBlock(stepzero); { - printRuntimeError(cs, func->getArguments()[1], "Range step had value of zero\n", { }); + printRuntimeError(cs, func->getArguments()[1], "range step had value of zero\n", { }); } cs->irb.setCurrentBlock(stepnotpos); { - printRuntimeError(cs, func->getArguments()[1], "Range had negative step value ('%ld'); invalid when start < end\n", { step }); + printRuntimeError(cs, func->getArguments()[1], "range had negative step value ('%ld'); invalid when start < end\n", { step }); } cs->irb.setCurrentBlock(stepnotneg); { - printRuntimeError(cs, func->getArguments()[1], "Range had positive step value ('%ld'); invalid when start > end\n", { step }); + printRuntimeError(cs, func->getArguments()[1], "range had positive step value ('%ld'); invalid when start > end\n", { step }); } } @@ -199,6 +204,47 @@ namespace misc return fn; } + + + using Idt = Identifier; + Idt getOI(const std::string& name, fir::Type* t = 0) + { + if(t) return util::obfuscateIdentifier(name, t->encodedStr()); + else return util::obfuscateIdentifier(name); + } + + Idt getCompare_FName(fir::Type* t) { return getOI("compare", t); } + Idt getSetElements_FName(fir::Type* t) { return getOI("setelements", t); } + Idt getCallClassConstructor_FName(fir::Type* t) { return getOI("callclassinit", t); } + Idt getSetElementsDefault_FName(fir::Type* t) { return getOI("setelementsdefault", t); } + + Idt getClone_FName(fir::Type* t) { return getOI("clone", t); } + Idt getAppend_FName(fir::Type* t) { return getOI("append", t); } + Idt getPopBack_FName(fir::Type* t) { return getOI("popback", t); } + Idt getMakeFromOne_FName(fir::Type* t) { return getOI("makefromone", t); } + Idt getMakeFromTwo_FName(fir::Type* t) { return getOI("makefromtwo", t); } + Idt getReserveExtra_FName(fir::Type* t) { return getOI("reserveextra", t); } + Idt getAppendElement_FName(fir::Type* t) { return getOI("appendelement", t); } + Idt getReserveEnough_FName(fir::Type* t) { return getOI("reservesufficient", t); } + Idt getRecursiveRefcount_FName(fir::Type* t, bool incr) + { + return getOI(strprintf("rrc_%s", incr ? "incr" : "decr"), t); + } + + Idt getIncrRefcount_FName(fir::Type* t) { return getOI("incr_rc", t); } + Idt getDecrRefcount_FName(fir::Type* t) { return getOI("decr_rc", t); } + Idt getLoopIncrRefcount_FName(fir::Type* t) { return getOI("loop_incr_rc", t); } + Idt getLoopDecrRefcount_FName(fir::Type* t) { return getOI("loop_decr_rc", t); } + + Idt getCreateAnyOf_FName(fir::Type* t) { return getOI("create_any_of", t); } + Idt getGetValueFromAny_FName(fir::Type* t) { return getOI("get_value_from_any", t); } + + Idt getUtf8Length_FName() { return getOI("utf8_length"); } + Idt getRangeSanityCheck_FName() { return getOI("range_sanity"); } + Idt getMallocWrapper_FName() { return getOI("malloc_wrapper"); } + Idt getBoundsCheck_FName() { return getOI("boundscheck"); } + Idt getDecompBoundsCheck_FName() { return getOI("boundscheck_decomp"); } + } } } diff --git a/source/codegen/glue/saa_common.cpp b/source/codegen/glue/saa_common.cpp index 70a8e1fb..2d83081d 100644 --- a/source/codegen/glue/saa_common.cpp +++ b/source/codegen/glue/saa_common.cpp @@ -1,11 +1,12 @@ // saa_common.cpp -// Copyright (c) 2017, zhiayang@gmail.com +// Copyright (c) 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "codegen.h" #include "platform.h" #include "gluecode.h" +#include "frontend.h" // namespace cgn::glue::saa_common namespace cgn { @@ -15,7 +16,7 @@ namespace saa_common static inline bool isSAA(fir::Type* t) { return t->isStringType() || t->isDynamicArrayType(); } static inline fir::Type* getSAAElm(fir::Type* t) { iceAssert(isSAA(t)); return (t->isStringType() ? fir::Type::getInt8() : t->getArrayElementType()); } static inline fir::Type* getSAASlice(fir::Type* t, bool mut = true) { iceAssert(isSAA(t)); return fir::ArraySliceType::get(getSAAElm(t), mut); } - static inline fir::ConstantInt* getCI(int64_t i) { return fir::ConstantInt::getInt64(i); } + static inline fir::ConstantInt* getCI(int64_t i) { return fir::ConstantInt::getNative(i); } static fir::Value* castRawBufToElmPtr(CodegenState* cs, fir::Type* saa, fir::Value* buf) { @@ -32,15 +33,15 @@ namespace saa_common { iceAssert(fir::isRefCountedType(elm)); - auto fname = "__loop_incr_rc_" + elm->str(); - fir::Function* retfn = cs->module->getFunction(Identifier(fname, IdKind::Name)); + auto fname = misc::getLoopIncrRefcount_FName(elm); + fir::Function* retfn = cs->module->getFunction(fname); if(!retfn) { auto restore = cs->irb.getCurrentBlock(); - fir::Function* func = cs->module->getOrCreateFunction(Identifier(fname, IdKind::Name), - fir::FunctionType::get({ elm->getPointerTo(), fir::Type::getInt64() }, fir::Type::getVoid()), fir::LinkageType::Internal); + fir::Function* func = cs->module->getOrCreateFunction(fname, + fir::FunctionType::get({ elm->getPointerTo(), fir::Type::getNativeWord() }, fir::Type::getVoid()), fir::LinkageType::Internal); func->setAlwaysInline(); @@ -51,10 +52,8 @@ namespace saa_common fir::IRBlock* body = cs->irb.addNewBlockInFunction("loopBody", func); fir::IRBlock* merge = cs->irb.addNewBlockInFunction("merge", func); - fir::Value* ctrPtr = cs->irb.StackAlloc(fir::Type::getInt64()); + fir::Value* ctrPtr = cs->irb.StackAlloc(fir::Type::getNativeWord()); - // already set to 0 internally - // cs->irb.WritePtr(fir::ConstantInt::getInt64(0), ctrPtr); fir::Value* s2ptr = func->getArguments()[0]; fir::Value* s2len = func->getArguments()[1]; @@ -72,12 +71,12 @@ namespace saa_common cs->irb.setCurrentBlock(body); { // increment refcount - fir::Value* val = cs->irb.ReadPtr(cs->irb.PointerAdd(s2ptr, cs->irb.ReadPtr(ctrPtr))); + fir::Value* val = cs->irb.ReadPtr(cs->irb.GetPointer(s2ptr, cs->irb.ReadPtr(ctrPtr))); cs->incrementRefCount(val); // increment counter - cs->irb.WritePtr(cs->irb.Add(fir::ConstantInt::getInt64(1), cs->irb.ReadPtr(ctrPtr)), ctrPtr); + cs->irb.WritePtr(cs->irb.Add(fir::ConstantInt::getNative(1), cs->irb.ReadPtr(ctrPtr)), ctrPtr); cs->irb.UnCondBranch(cond); } @@ -98,10 +97,10 @@ namespace saa_common iceAssert(rc->getType()->isIntegerType() && "not integer type"); auto rcp = cs->irb.Call(cs->getOrDeclareLibCFunction(ALLOCATE_MEMORY_FUNC), getCI(REFCOUNT_SIZE)); - rcp = cs->irb.PointerTypeCast(rcp, fir::Type::getMutInt64Ptr()); + rcp = cs->irb.PointerTypeCast(rcp, fir::Type::getNativeWordPtr()->getMutablePointerVersion()); cs->irb.WritePtr(rc, rcp); - return cs->irb.PointerTypeCast(rcp, fir::Type::getInt64Ptr()); + return cs->irb.PointerTypeCast(rcp, fir::Type::getNativeWordPtr()); } fir::Value* initSAAWithRefCount(CodegenState* cs, fir::Value* saa, fir::Value* rc) @@ -137,7 +136,7 @@ namespace saa_common fir::IRBlock* loopbody = cs->irb.addNewBlockInFunction("loopbody", curfunc); fir::IRBlock* merge = cs->irb.addNewBlockInFunction("merge", curfunc); - fir::Value* counter = cs->irb.StackAlloc(fir::Type::getInt64()); + fir::Value* counter = cs->irb.StackAlloc(fir::Type::getNativeWord()); cs->irb.WritePtr(startIndex, counter); cs->irb.UnCondBranch(loopcond); @@ -150,20 +149,20 @@ namespace saa_common cs->irb.setCurrentBlock(loopbody); { // make clone - fir::Value* origElm = cs->irb.PointerAdd(ptr, cs->irb.ReadPtr(counter)); + fir::Value* origElm = cs->irb.GetPointer(ptr, cs->irb.ReadPtr(counter)); fir::Value* clone = 0; //* note: the '0' argument specifies the offset to clone from -- since want the whole thing, the offset is 0. auto elm = cs->irb.ReadPtr(origElm); - clone = cs->irb.Call(fn, isSAA(elm->getType()) ? cs->irb.CreateSliceFromSAA(elm, false) : elm, fir::ConstantInt::getInt64(0)); + clone = cs->irb.Call(fn, isSAA(elm->getType()) ? cs->irb.CreateSliceFromSAA(elm, false) : elm, fir::ConstantInt::getNative(0)); // store clone - fir::Value* newElm = cs->irb.PointerAdd(newptr, cs->irb.ReadPtr(counter)); + fir::Value* newElm = cs->irb.GetPointer(newptr, cs->irb.ReadPtr(counter)); cs->irb.WritePtr(clone, newElm); // increment counter - cs->irb.WritePtr(cs->irb.Add(cs->irb.ReadPtr(counter), fir::ConstantInt::getInt64(1)), counter); + cs->irb.WritePtr(cs->irb.Add(cs->irb.ReadPtr(counter), fir::ConstantInt::getNative(1)), counter); cs->irb.UnCondBranch(loopcond); } @@ -177,7 +176,7 @@ namespace saa_common { fir::Function* memcpyf = cs->module->getIntrinsicFunction("memmove"); - cs->irb.Call(memcpyf, { newptr, cs->irb.PointerTypeCast(cs->irb.PointerAdd(oldptr, + cs->irb.Call(memcpyf, { newptr, cs->irb.PointerTypeCast(cs->irb.GetPointer(oldptr, startIndex), fir::Type::getMutInt8Ptr()), bytecount, fir::ConstantBool::get(false) }); #if DEBUG_ARRAY_ALLOCATION | DEBUG_STRING_ALLOCATION @@ -223,7 +222,7 @@ namespace saa_common fir::Function* memcpyf = cs->module->getIntrinsicFunction("memmove"); - cs->irb.Call(memcpyf, { newptr, cs->irb.PointerTypeCast(cs->irb.PointerAdd(oldptr, + cs->irb.Call(memcpyf, { newptr, cs->irb.PointerTypeCast(cs->irb.GetPointer(oldptr, startIndex), fir::Type::getMutInt8Ptr()), bytecount, fir::ConstantBool::get(false) }); } else @@ -239,7 +238,7 @@ namespace saa_common fir::Function* generateCloneFunction(CodegenState* cs, fir::Type* _saa) { - auto fname = "__clone_" + _saa->str(); + auto fname = misc::getClone_FName(_saa); iceAssert(isSAA(_saa) || _saa->isArraySliceType()); auto slicetype = (isSAA(_saa) ? getSAASlice(_saa, false) : fir::ArraySliceType::get(_saa->getArrayElementType(), false)); @@ -249,14 +248,14 @@ namespace saa_common fir::Type* outtype = (isSAA(_saa) ? _saa : fir::DynamicArrayType::get(slicetype->getArrayElementType())); - fir::Function* retfn = cs->module->getFunction(Identifier(fname, IdKind::Name)); + fir::Function* retfn = cs->module->getFunction(fname); if(!retfn) { auto restore = cs->irb.getCurrentBlock(); - fir::Function* func = cs->module->getOrCreateFunction(Identifier(fname, IdKind::Name), - fir::FunctionType::get({ slicetype, fir::Type::getInt64() }, outtype), fir::LinkageType::Internal); + fir::Function* func = cs->module->getOrCreateFunction(fname, + fir::FunctionType::get({ slicetype, fir::Type::getNativeWord() }, outtype), fir::LinkageType::Internal); func->setAlwaysInline(); @@ -295,7 +294,7 @@ namespace saa_common // null terminator if(!isArray) - cs->irb.WritePtr(fir::ConstantInt::getInt8(0), cs->irb.PointerAdd(newbuf, lhsbytecount)); + cs->irb.WritePtr(fir::ConstantInt::getInt8(0), cs->irb.GetPointer(newbuf, lhsbytecount)); } @@ -346,16 +345,16 @@ namespace saa_common fir::Function* generateAppendFunction(CodegenState* cs, fir::Type* saa) { - auto fname = "__append_" + saa->str(); + auto fname = misc::getAppend_FName(saa); iceAssert(isSAA(saa)); - fir::Function* retfn = cs->module->getFunction(Identifier(fname, IdKind::Name)); + fir::Function* retfn = cs->module->getFunction(fname); if(!retfn) { auto restore = cs->irb.getCurrentBlock(); - fir::Function* func = cs->module->getOrCreateFunction(Identifier(fname, IdKind::Name), + fir::Function* func = cs->module->getOrCreateFunction(fname, fir::FunctionType::get({ saa, getSAASlice(saa, false) }, saa), fir::LinkageType::Internal); func->setAlwaysInline(); @@ -380,7 +379,7 @@ namespace saa_common { fir::Function* memcpyf = cs->module->getIntrinsicFunction("memmove"); cs->irb.Call(memcpyf, { - cs->irb.PointerTypeCast(cs->irb.PointerAdd(lhsbuf, lhslen), fir::Type::getMutInt8Ptr()), + cs->irb.PointerTypeCast(cs->irb.GetPointer(lhsbuf, lhslen), fir::Type::getMutInt8Ptr()), cs->irb.PointerTypeCast(rhsbuf, fir::Type::getMutInt8Ptr()), rhsbytecount, fir::ConstantBool::get(false) }); @@ -388,7 +387,7 @@ namespace saa_common // null terminator if(saa->isStringType()) { - cs->irb.WritePtr(fir::ConstantInt::getInt8(0), cs->irb.PointerTypeCast(cs->irb.PointerAdd(lhsbuf, cs->irb.Add(lhslen, rhslen)), + cs->irb.WritePtr(fir::ConstantInt::getInt8(0), cs->irb.PointerTypeCast(cs->irb.GetPointer(lhsbuf, cs->irb.Add(lhslen, rhslen)), fir::Type::getMutInt8Ptr())); } } @@ -418,16 +417,16 @@ namespace saa_common fir::Function* generateElementAppendFunction(CodegenState* cs, fir::Type* saa) { - auto fname = "__append_elm_" + saa->str(); + auto fname = misc::getAppendElement_FName(saa); iceAssert(isSAA(saa)); - fir::Function* retfn = cs->module->getFunction(Identifier(fname, IdKind::Name)); + fir::Function* retfn = cs->module->getFunction(fname); if(!retfn) { auto restore = cs->irb.getCurrentBlock(); - fir::Function* func = cs->module->getOrCreateFunction(Identifier(fname, IdKind::Name), + fir::Function* func = cs->module->getOrCreateFunction(fname, fir::FunctionType::get({ saa, getSAAElm(saa) }, saa), fir::LinkageType::Internal); func->setAlwaysInline(); @@ -463,16 +462,16 @@ namespace saa_common fir::Function* generateConstructFromTwoFunction(CodegenState* cs, fir::Type* saa) { - auto fname = "__construct_fromtwo_" + saa->str(); + auto fname = misc::getMakeFromTwo_FName(saa); iceAssert(isSAA(saa)); - fir::Function* retfn = cs->module->getFunction(Identifier(fname, IdKind::Name)); + fir::Function* retfn = cs->module->getFunction(fname); if(!retfn) { auto restore = cs->irb.getCurrentBlock(); - fir::Function* func = cs->module->getOrCreateFunction(Identifier(fname, IdKind::Name), + fir::Function* func = cs->module->getOrCreateFunction(fname, fir::FunctionType::get({ getSAASlice(saa, false), getSAASlice(saa, false) }, saa), fir::LinkageType::Internal); func->setAlwaysInline(); @@ -494,7 +493,7 @@ namespace saa_common ret = cs->irb.SetSAAData(ret, castRawBufToElmPtr(cs, saa, getCI(0))); ret = cs->irb.SetSAALength(ret, getCI(0)); ret = cs->irb.SetSAACapacity(ret, getCI(0)); //? vv we count on the 'reserveAtLeast' function to init our refcount - ret = cs->irb.SetSAARefCountPointer(ret, fir::ConstantValue::getZeroValue(fir::Type::getInt64Ptr())); + ret = cs->irb.SetSAARefCountPointer(ret, fir::ConstantValue::getZeroValue(fir::Type::getNativeWordPtr())); ret = cs->irb.Call(generateReserveAtLeastFunction(cs, saa), ret, cs->irb.Add(cs->irb.Add(lhslen, rhslen), @@ -524,14 +523,14 @@ namespace saa_common lhsbytecount, fir::ConstantBool::get(false) }); - cs->irb.Call(memcpyf, { cs->irb.PointerAdd(rawbuf, lhsbytecount), rawrhsbuf, + cs->irb.Call(memcpyf, { cs->irb.GetPointer(rawbuf, lhsbytecount), rawrhsbuf, rhsbytecount, fir::ConstantBool::get(false) }); // if it's a string, again, null terminator. if(saa->isStringType()) { - cs->irb.WritePtr(fir::ConstantInt::getInt8(0), cs->irb.PointerAdd(rawbuf, cs->irb.Add(lhsbytecount, rhsbytecount))); + cs->irb.WritePtr(fir::ConstantInt::getInt8(0), cs->irb.GetPointer(rawbuf, cs->irb.Add(lhsbytecount, rhsbytecount))); } else if(fir::isRefCountedType(getSAAElm(saa))) { @@ -559,16 +558,16 @@ namespace saa_common fir::Function* generateConstructWithElementFunction(CodegenState* cs, fir::Type* saa) { - auto fname = "__construct_withelm_" + saa->str(); + auto fname = misc::getMakeFromOne_FName(saa); iceAssert(isSAA(saa)); - fir::Function* retfn = cs->module->getFunction(Identifier(fname, IdKind::Name)); + fir::Function* retfn = cs->module->getFunction(fname); if(!retfn) { auto restore = cs->irb.getCurrentBlock(); - fir::Function* func = cs->module->getOrCreateFunction(Identifier(fname, IdKind::Name), + fir::Function* func = cs->module->getOrCreateFunction(fname, fir::FunctionType::get({ getSAASlice(saa), getSAAElm(saa) }, saa), fir::LinkageType::Internal); func->setAlwaysInline(); @@ -607,17 +606,17 @@ namespace saa_common fir::Function* generateReserveAtLeastFunction(CodegenState* cs, fir::Type* saa) { - auto fname = "__reserve_atleast_" + saa->str(); + auto fname = misc::getReserveEnough_FName(saa); iceAssert(isSAA(saa)); - fir::Function* retfn = cs->module->getFunction(Identifier(fname, IdKind::Name)); + fir::Function* retfn = cs->module->getFunction(fname); if(!retfn) { auto restore = cs->irb.getCurrentBlock(); - fir::Function* func = cs->module->getOrCreateFunction(Identifier(fname, IdKind::Name), - fir::FunctionType::get({ saa, fir::Type::getInt64() }, saa), fir::LinkageType::Internal); + fir::Function* func = cs->module->getOrCreateFunction(fname, + fir::FunctionType::get({ saa, fir::Type::getNativeWord() }, saa), fir::LinkageType::Internal); func->setAlwaysInline(); @@ -636,7 +635,7 @@ namespace saa_common fir::IRBlock* mergerc = cs->irb.addNewBlockInFunction("mergerc", func); auto oldrcp = cs->irb.GetSAARefCountPointer(s1, "oldrcp"); - cs->irb.CondBranch(cs->irb.ICmpEQ(oldrcp, cs->irb.IntToPointerCast(getCI(0), fir::Type::getInt64Ptr())), nullrc, notnullrc); + cs->irb.CondBranch(cs->irb.ICmpEQ(oldrcp, cs->irb.IntToPointerCast(getCI(0), fir::Type::getNativeWordPtr())), nullrc, notnullrc); //? these phi nodes are for the refcount pointer of the thing we will eventually return. @@ -658,7 +657,7 @@ namespace saa_common cs->irb.setCurrentBlock(mergerc); { - auto rcptr = cs->irb.CreatePHINode(fir::Type::getInt64Ptr()); + auto rcptr = cs->irb.CreatePHINode(fir::Type::getNativeWordPtr()); rcptr->addIncoming(nullphi, nullrc); rcptr->addIncoming(notnullphi, notnullrc); @@ -685,7 +684,7 @@ namespace saa_common // null terminator if(saa->isStringType()) - cs->irb.WritePtr(fir::ConstantInt::getInt8(0), cs->irb.PointerAdd(newbuf, cs->irb.Subtract(newbytecount, getCI(1)))); + cs->irb.WritePtr(fir::ConstantInt::getInt8(0), cs->irb.GetPointer(newbuf, cs->irb.Subtract(newbytecount, getCI(1)))); auto ret = cs->irb.CreateValue(saa); ret = cs->irb.SetSAAData(ret, newbuf); @@ -723,17 +722,17 @@ namespace saa_common { // we can just do this in terms of reserveAtLeast. - auto fname = "__reserve_extra" + saa->str(); + auto fname = misc::getReserveExtra_FName(saa); iceAssert(isSAA(saa)); - fir::Function* retfn = cs->module->getFunction(Identifier(fname, IdKind::Name)); + fir::Function* retfn = cs->module->getFunction(fname); if(!retfn) { auto restore = cs->irb.getCurrentBlock(); - fir::Function* func = cs->module->getOrCreateFunction(Identifier(fname, IdKind::Name), - fir::FunctionType::get({ saa, fir::Type::getInt64() }, saa), fir::LinkageType::Internal); + fir::Function* func = cs->module->getOrCreateFunction(fname, + fir::FunctionType::get({ saa, fir::Type::getNativeWord() }, saa), fir::LinkageType::Internal); func->setAlwaysInline(); @@ -760,16 +759,18 @@ namespace saa_common fir::Function* generateBoundsCheckFunction(CodegenState* cs, bool isString, bool isDecomp) { - auto fname = (isDecomp ? "__boundscheck_" : "__boundscheck_decomp_"); + if(frontend::getIsNoRuntimeChecks()) + return 0; - fir::Function* retfn = cs->module->getFunction(Identifier(fname, IdKind::Name)); + auto fname = (isDecomp ? misc::getDecompBoundsCheck_FName() : misc::getBoundsCheck_FName()); + fir::Function* retfn = cs->module->getFunction(fname); if(!retfn) { auto restore = cs->irb.getCurrentBlock(); - fir::Function* func = cs->module->getOrCreateFunction(Identifier(fname, IdKind::Name), - fir::FunctionType::get({ fir::Type::getInt64(), fir::Type::getInt64(), fir::Type::getCharSlice(false) }, + fir::Function* func = cs->module->getOrCreateFunction(fname, + fir::FunctionType::get({ fir::Type::getNativeWord(), fir::Type::getNativeWord(), fir::Type::getCharSlice(false) }, fir::Type::getVoid()), fir::LinkageType::Internal); fir::IRBlock* entry = cs->irb.addNewBlockInFunction("entry", func); @@ -793,19 +794,19 @@ namespace saa_common { if(isDecomp) { - printRuntimeError(cs, func->getArguments()[2], "Index '%ld' out of bounds for " + printRuntimeError(cs, func->getArguments()[2], "index '%ld' out of bounds for " + std::string(isString ? "string" : "array") + " of length %ld\n", { ind, max }); } else { - printRuntimeError(cs, func->getArguments()[2], "Binding of '%ld' " + printRuntimeError(cs, func->getArguments()[2], "binding of '%ld' " + std::string(isString ? "chars" : "elements") + " out of bounds for string of length %ld\n", { ind, max }); } } cs->irb.setCurrentBlock(checkneg); { - fir::Value* res = cs->irb.ICmpLT(ind, fir::ConstantInt::getInt64(0)); + fir::Value* res = cs->irb.ICmpLT(ind, fir::ConstantInt::getNative(0)); cs->irb.CondBranch(res, failb, merge); } diff --git a/source/codegen/glue/strings.cpp b/source/codegen/glue/strings.cpp index caf88fa3..6289251b 100644 --- a/source/codegen/glue/strings.cpp +++ b/source/codegen/glue/strings.cpp @@ -1,5 +1,5 @@ // strings.cpp -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "codegen.h" @@ -44,17 +44,17 @@ namespace string fir::Function* getCompareFunction(CodegenState* cs) { - auto fname = "__compare_" + fir::Type::getString()->str(); - fir::Function* cmpf = cs->module->getFunction(Identifier(fname, IdKind::Name)); + auto fname = misc::getCompare_FName(fir::Type::getString()); + fir::Function* cmpf = cs->module->getFunction(fname); if(!cmpf) { // great. auto restore = cs->irb.getCurrentBlock(); - fir::Function* func = cs->module->getOrCreateFunction(Identifier(fname, IdKind::Name), + fir::Function* func = cs->module->getOrCreateFunction(fname, fir::FunctionType::get({ fir::Type::getString(), fir::Type::getString() }, - fir::Type::getInt64()), fir::LinkageType::Internal); + fir::Type::getNativeWord()), fir::LinkageType::Internal); func->setAlwaysInline(); @@ -109,8 +109,8 @@ namespace string cs->irb.setCurrentBlock(loopincr); { // increment str1 and str2 - fir::Value* v1 = cs->irb.PointerAdd(str1, fir::ConstantInt::getInt64(1)); - fir::Value* v2 = cs->irb.PointerAdd(str2, fir::ConstantInt::getInt64(1)); + fir::Value* v1 = cs->irb.GetPointer(str1, fir::ConstantInt::getNative(1)); + fir::Value* v2 = cs->irb.GetPointer(str2, fir::ConstantInt::getNative(1)); cs->irb.WritePtr(v1, str1p); cs->irb.WritePtr(v2, str2p); @@ -145,13 +145,13 @@ namespace string fir::IRBlock* merge = cs->irb.addNewBlockInFunction("merge", func); fir::IRBlock* dorc = cs->irb.addNewBlockInFunction("dorc", func); - cs->irb.CondBranch(cs->irb.ICmpEQ(rcp, fir::ConstantValue::getZeroValue(fir::Type::getInt64Ptr())), + cs->irb.CondBranch(cs->irb.ICmpEQ(rcp, fir::ConstantValue::getZeroValue(fir::Type::getNativeWordPtr())), merge, dorc); cs->irb.setCurrentBlock(dorc); { auto oldrc = cs->irb.ReadPtr(rcp, "oldrc"); - auto newrc = cs->irb.Add(oldrc, fir::ConstantInt::getInt64(decrement ? -1 : 1)); + auto newrc = cs->irb.Add(oldrc, fir::ConstantInt::getNative(decrement ? -1 : 1)); cs->irb.SetSAARefCount(str, newrc); @@ -167,7 +167,7 @@ namespace string if(decrement) { fir::IRBlock* dofree = cs->irb.addNewBlockInFunction("dofree", func); - cs->irb.CondBranch(cs->irb.ICmpEQ(newrc, fir::ConstantInt::getInt64(0)), + cs->irb.CondBranch(cs->irb.ICmpEQ(newrc, fir::ConstantInt::getNative(0)), dofree, merge); cs->irb.setCurrentBlock(dofree); @@ -204,14 +204,14 @@ namespace string fir::Function* getRefCountIncrementFunction(CodegenState* cs) { - auto fname = "__incr_rc_" + fir::Type::getString()->str(); - fir::Function* retfn = cs->module->getFunction(Identifier(fname, IdKind::Name)); + auto fname = misc::getIncrRefcount_FName(fir::Type::getString()); + fir::Function* retfn = cs->module->getFunction(fname); if(!retfn) { auto restore = cs->irb.getCurrentBlock(); - fir::Function* func = cs->module->getOrCreateFunction(Identifier(fname, IdKind::Name), + fir::Function* func = cs->module->getOrCreateFunction(fname, fir::FunctionType::get({ fir::Type::getString() }, fir::Type::getVoid()), fir::LinkageType::Internal); func->setAlwaysInline(); @@ -231,14 +231,14 @@ namespace string fir::Function* getRefCountDecrementFunction(CodegenState* cs) { - auto fname = "__decr_rc_" + fir::Type::getString()->str(); - fir::Function* retfn = cs->module->getFunction(Identifier(fname, IdKind::Name)); + auto fname = misc::getDecrRefcount_FName(fir::Type::getString()); + fir::Function* retfn = cs->module->getFunction(fname); if(!retfn) { auto restore = cs->irb.getCurrentBlock(); - fir::Function* func = cs->module->getOrCreateFunction(Identifier(fname, IdKind::Name), + fir::Function* func = cs->module->getOrCreateFunction(fname, fir::FunctionType::get({ fir::Type::getString() }, fir::Type::getVoid()), fir::LinkageType::Internal); func->setAlwaysInline(); @@ -262,15 +262,15 @@ namespace string fir::Function* getUnicodeLengthFunction(CodegenState* cs) { - auto fname = "__unicode_length_" + fir::Type::getString()->str(); - fir::Function* lenf = cs->module->getFunction(Identifier(fname, IdKind::Name)); + auto fname = misc::getUtf8Length_FName(); + fir::Function* lenf = cs->module->getFunction(fname); if(!lenf) { auto restore = cs->irb.getCurrentBlock(); - fir::Function* func = cs->module->getOrCreateFunction(Identifier(fname, IdKind::Name), - fir::FunctionType::get({ fir::Type::getInt8Ptr() }, fir::Type::getInt64()), fir::LinkageType::Internal); + fir::Function* func = cs->module->getOrCreateFunction(fname, + fir::FunctionType::get({ fir::Type::getInt8Ptr() }, fir::Type::getNativeWord()), fir::LinkageType::Internal); func->setAlwaysInline(); @@ -304,11 +304,11 @@ namespace string return len } */ - auto i0 = fir::ConstantInt::getInt64(0); - auto i1 = fir::ConstantInt::getInt64(1); + auto i0 = fir::ConstantInt::getNative(0); + auto i1 = fir::ConstantInt::getNative(1); auto c0 = fir::ConstantInt::getInt8(0); - fir::Value* lenp = cs->irb.StackAlloc(fir::Type::getInt64()); + fir::Value* lenp = cs->irb.StackAlloc(fir::Type::getNativeWord()); cs->irb.WritePtr(i0, lenp); @@ -349,7 +349,7 @@ namespace string cs->irb.setCurrentBlock(skip); { - auto newptr = cs->irb.PointerAdd(cs->irb.ReadPtr(ptrp), i1); + auto newptr = cs->irb.GetPointer(cs->irb.ReadPtr(ptrp), i1); cs->irb.WritePtr(newptr, ptrp); cs->irb.UnCondBranch(cond); diff --git a/source/codegen/literals.cpp b/source/codegen/literals.cpp index 5d54cdf6..ac49d206 100644 --- a/source/codegen/literals.cpp +++ b/source/codegen/literals.cpp @@ -1,5 +1,5 @@ // literals.cpp -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "sst.h" @@ -24,7 +24,7 @@ CGResult sst::LiteralNumber::_codegen(cgn::CodegenState* cs, fir::Type* infer) return CGResult(fir::ConstantFP::get(this->type, this->num.toDouble())); else - return CGResult(fir::ConstantInt::get(this->type, this->num.toLLong())); + return CGResult(fir::ConstantInt::get(this->type, this->type->isSignedIntType() ? this->num.toLLong() : this->num.toULLong())); } } @@ -37,17 +37,17 @@ CGResult sst::LiteralArray::_codegen(cgn::CodegenState* cs, fir::Type* infer) { auto elmty = this->type->toArrayType()->getElementType(); if(fir::isRefCountedType(elmty)) - error(this, "Cannot have refcounted type in array literal"); + error(this, "cannot have refcounted type in array literal"); std::vector vals; for(auto v : this->values) { auto cv = dcast(fir::ConstantValue, v->codegen(cs, elmty).value); if(!cv) - error(v, "Constant value required in fixed array literal"); + error(v, "constant value required in fixed array literal"); if(cv->getType() != elmty) - error(v, "Mismatched type for array literal; expected element type '%s', found '%s'", elmty, cv->getType()); + error(v, "mismatched type for array literal; expected element type '%s', found '%s'", elmty, cv->getType()); vals.push_back(cv); } @@ -65,35 +65,29 @@ CGResult sst::LiteralArray::_codegen(cgn::CodegenState* cs, fir::Type* infer) if(this->values.empty()) { // if our element type is void, and there is no infer... die. - if((elmty->isVoidType() && infer == 0) || (infer && infer->getArrayElementType()->isVoidType())) - error(this, "Failed to infer type for empty array literal"); - - //! by right, if we have no values, then elmty is *supposed* to be void. - iceAssert(elmty->isVoidType() && infer); + if(!infer || (elmty->isVoidType() && infer == 0) || (infer && infer->getArrayElementType()->isVoidType())) + error(this, "failed to infer type for empty array literal"); + auto z = fir::ConstantInt::getNative(0); if(infer->isDynamicArrayType()) { // ok. elmty = infer->getArrayElementType(); - // error(this, "elmty = %s", elmty); - auto z = fir::ConstantInt::getInt64(0); - return CGResult(fir::ConstantDynamicArray::get(fir::DynamicArrayType::get(elmty), fir::ConstantValue::getZeroValue(elmty->getPointerTo()), - z, z)); + return CGResult(fir::ConstantDynamicArray::get(fir::DynamicArrayType::get(elmty), + fir::ConstantValue::getZeroValue(elmty->getPointerTo()), z, z)); } else if(infer->isArraySliceType()) { elmty = infer->getArrayElementType(); - auto z = fir::ConstantInt::getInt64(0); - //* note: it's clearly a null pointer, so it must be immutable. return CGResult(fir::ConstantArraySlice::get(fir::ArraySliceType::get(elmty, false), fir::ConstantValue::getZeroValue(elmty->getPointerTo()), z)); } else { - error(this, "Incorrectly inferred type '%s' for empty array literal", infer); + error(this, "incorrectly inferred type '%s' for empty array literal", infer); } } @@ -109,7 +103,7 @@ CGResult sst::LiteralArray::_codegen(cgn::CodegenState* cs, fir::Type* infer) { auto restore = cs->irb.getCurrentBlock(); - fir::Function* func = cs->module->getOrCreateFunction(Identifier("__init_array_" + std::to_string(_id - 1), IdKind::Name), + fir::Function* func = cs->module->getOrCreateFunction(util::obfuscateIdentifier("init_array", _id - 1), fir::FunctionType::get({ }, fir::Type::getVoid()), fir::LinkageType::Internal); fir::IRBlock* entry = cs->irb.addNewBlockInFunction("entry", func); @@ -126,7 +120,7 @@ CGResult sst::LiteralArray::_codegen(cgn::CodegenState* cs, fir::Type* infer) if(vl->getType() != elmty) { - error(v, "Mismatched type for array literal; expected element type '%s', found '%s'", + error(v, "mismatched type for array literal; expected element type '%s', found '%s'", elmty, vl->getType()); } @@ -158,9 +152,9 @@ CGResult sst::LiteralArray::_codegen(cgn::CodegenState* cs, fir::Type* infer) auto aa = cs->irb.CreateValue(this->type->toDynamicArrayType()); aa = cs->irb.SetSAAData(aa, cs->irb.ConstGEP2(arrptr, 0, 0)); - aa = cs->irb.SetSAALength(aa, fir::ConstantInt::getInt64(this->values.size())); - aa = cs->irb.SetSAACapacity(aa, fir::ConstantInt::getInt64(-1)); - aa = cs->irb.SetSAARefCountPointer(aa, fir::ConstantValue::getZeroValue(fir::Type::getInt64Ptr())); + aa = cs->irb.SetSAALength(aa, fir::ConstantInt::getNative(this->values.size())); + aa = cs->irb.SetSAACapacity(aa, fir::ConstantInt::getNative(-1)); + aa = cs->irb.SetSAARefCountPointer(aa, fir::ConstantValue::getZeroValue(fir::Type::getNativeWordPtr())); return CGResult(aa); } @@ -171,7 +165,7 @@ CGResult sst::LiteralArray::_codegen(cgn::CodegenState* cs, fir::Type* infer) auto aa = cs->irb.CreateValue(this->type->toArraySliceType()); aa = cs->irb.SetArraySliceData(aa, cs->irb.PointerTypeCast(cs->irb.ConstGEP2(arrptr, 0, 0), elmty->getPointerTo())); - aa = cs->irb.SetArraySliceLength(aa, fir::ConstantInt::getInt64(this->values.size())); + aa = cs->irb.SetArraySliceLength(aa, fir::ConstantInt::getNative(this->values.size())); return CGResult(aa); } @@ -206,7 +200,7 @@ CGResult sst::LiteralTuple::_codegen(cgn::CodegenState* cs, fir::Type* infer) if(vr->getType() != ty) { - error(this->values[i], "Mismatched types in tuple element %zu; expected type '%s', found type '%s'", + error(this->values[i], "mismatched types in tuple element %zu; expected type '%s', found type '%s'", i, ty, vr->getType()); } @@ -253,13 +247,21 @@ CGResult sst::LiteralBool::_codegen(cgn::CodegenState* cs, fir::Type* infer) return CGResult(fir::ConstantBool::get(this->value)); } +CGResult sst::LiteralChar::_codegen(cgn::CodegenState* cs, fir::Type* infer) +{ + cs->pushLoc(this); + defer(cs->popLoc()); + + return CGResult(fir::ConstantInt::getInt8((int8_t) this->value)); +} + CGResult sst::LiteralString::_codegen(cgn::CodegenState* cs, fir::Type* infer) { cs->pushLoc(this); defer(cs->popLoc()); // allow automatic coercion of string literals into i8* - if(this->isCString || (infer && infer == fir::Type::getInt8Ptr())) + if(this->isCString || infer == fir::Type::getInt8Ptr()) { // good old i8* fir::Value* stringVal = cs->module->createGlobalString(this->str); @@ -268,7 +270,7 @@ CGResult sst::LiteralString::_codegen(cgn::CodegenState* cs, fir::Type* infer) else { auto str = cs->module->createGlobalString(this->str); - auto slc = fir::ConstantArraySlice::get(fir::Type::getCharSlice(false), str, fir::ConstantInt::getInt64(this->str.length())); + auto slc = fir::ConstantArraySlice::get(fir::Type::getCharSlice(false), str, fir::ConstantInt::getNative(this->str.length())); return CGResult(slc); } diff --git a/source/codegen/logical.cpp b/source/codegen/logical.cpp index ee1351f0..e96e2f8f 100644 --- a/source/codegen/logical.cpp +++ b/source/codegen/logical.cpp @@ -1,5 +1,5 @@ // logical.cpp -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "errors.h" @@ -23,7 +23,7 @@ namespace cgn fir::Value* left = b->left->codegen(cs, fir::Type::getBool()).value; if(!left->getType()->isBoolType()) - error(b->left, "Non-boolean type '%s' cannot be used as a conditional", left->getType()); + error(b->left, "non-boolean type '%s' cannot be used as a conditional", left->getType()); // ok, compare first. fir::Value* cmpres = cs->irb.ICmpEQ(left, fir::ConstantBool::get(true)); @@ -39,7 +39,7 @@ namespace cgn // ok, check the second fir::Value* right = b->right->codegen(cs, fir::Type::getBool()).value; if(!right->getType()->isBoolType()) - error(b->right, "Non-boolean type '%s' cannot be used as a conditional", right->getType()); + error(b->right, "non-boolean type '%s' cannot be used as a conditional", right->getType()); fir::Value* cmpres = cs->irb.ICmpEQ(right, fir::ConstantBool::get(true)); cs->irb.CondBranch(cmpres, pass, merge); @@ -71,7 +71,7 @@ namespace cgn fir::Value* left = b->left->codegen(cs, fir::Type::getBool()).value; if(!left->getType()->isBoolType()) - error(b->left, "Non-boolean type '%s' cannot be used as a conditional", left->getType()); + error(b->left, "non-boolean type '%s' cannot be used as a conditional", left->getType()); // ok, compare first. fir::Value* cmpres = cs->irb.ICmpEQ(left, fir::ConstantBool::get(true)); @@ -88,7 +88,7 @@ namespace cgn // ok, check the second fir::Value* right = b->right->codegen(cs, fir::Type::getBool()).value; if(!right->getType()->isBoolType()) - error(b->right, "Non-boolean type '%s' cannot be used as a conditional", right->getType()); + error(b->right, "non-boolean type '%s' cannot be used as a conditional", right->getType()); fir::Value* cmpres = cs->irb.ICmpEQ(right, fir::ConstantBool::get(true)); cs->irb.CondBranch(cmpres, merge, fail); diff --git a/source/codegen/loops.cpp b/source/codegen/loops.cpp index 8082eb3b..9f306a52 100644 --- a/source/codegen/loops.cpp +++ b/source/codegen/loops.cpp @@ -1,15 +1,24 @@ // loops.cpp -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "sst.h" #include "codegen.h" +#include "mpool.h" CGResult sst::WhileLoop::_codegen(cgn::CodegenState* cs, fir::Type* inferred) { cs->pushLoc(this); defer(cs->popLoc()); + if(this->isDoVariant && !this->cond) + { + this->body->codegen(cs); + return CGResult(0); + } + + + auto loop = cs->irb.addNewBlockAfter("loop-" + this->loc.shortString(), cs->irb.getCurrentBlock()); fir::IRBlock* merge = 0; @@ -144,8 +153,8 @@ CGResult sst::ForeachLoop::_codegen(cgn::CodegenState* cs, fir::Type* inferred) fir::Value* end = 0; fir::Value* step = 0; - fir::Value* idxptr = cs->irb.StackAlloc(fir::Type::getInt64()); - fir::Value* iterptr = cs->irb.StackAlloc(fir::Type::getInt64()); + fir::Value* idxptr = cs->irb.StackAlloc(fir::Type::getNativeWord()); + fir::Value* iterptr = cs->irb.StackAlloc(fir::Type::getNativeWord()); auto [ array, vk ] = this->array->codegen(cs); (void) vk; @@ -167,13 +176,13 @@ CGResult sst::ForeachLoop::_codegen(cgn::CodegenState* cs, fir::Type* inferred) //! note: again for negative ranges, we should be subtracting 1 instead. - end = cs->irb.Add(end, cs->irb.Select(cs->irb.ICmpGEQ(step, fir::ConstantInt::getInt64(0)), - fir::ConstantInt::getInt64(1), fir::ConstantInt::getInt64(-1))); + end = cs->irb.Add(end, cs->irb.Select(cs->irb.ICmpGEQ(step, fir::ConstantInt::getNative(0)), + fir::ConstantInt::getNative(1), fir::ConstantInt::getNative(-1))); } else { - cs->irb.WritePtr(fir::ConstantInt::getInt64(0), idxptr); - step = fir::ConstantInt::getInt64(1); + cs->irb.WritePtr(fir::ConstantInt::getNative(0), idxptr); + step = fir::ConstantInt::getNative(1); if(array->getType()->isDynamicArrayType()) { @@ -189,11 +198,11 @@ CGResult sst::ForeachLoop::_codegen(cgn::CodegenState* cs, fir::Type* inferred) } else if(array->getType()->isArrayType()) { - end = fir::ConstantInt::getInt64(array->getType()->toArrayType()->getArraySize()); + end = fir::ConstantInt::getNative(array->getType()->toArrayType()->getArraySize()); } else { - error(this->array, "Unsupported type '%s' in foreach loop", array->getType()); + error(this->array, "unsupported type '%s' in foreach loop", array->getType()); } } @@ -204,7 +213,7 @@ CGResult sst::ForeachLoop::_codegen(cgn::CodegenState* cs, fir::Type* inferred) fir::Value* cond = 0; if(array->getType()->isRangeType()) { - cond = cs->irb.Select(cs->irb.ICmpGT(step, fir::ConstantInt::getInt64(0)), + cond = cs->irb.Select(cs->irb.ICmpGT(step, fir::ConstantInt::getNative(0)), cs->irb.ICmpLT(cs->irb.ReadPtr(idxptr), end), // i < end for step > 0 cs->irb.ICmpGT(cs->irb.ReadPtr(idxptr), end)); // i > end for step < 0 } @@ -223,13 +232,13 @@ CGResult sst::ForeachLoop::_codegen(cgn::CodegenState* cs, fir::Type* inferred) theptr = idxptr; else if(array->getType()->isDynamicArrayType()) - theptr = cs->irb.PointerAdd(cs->irb.GetSAAData(array), cs->irb.ReadPtr(idxptr)); + theptr = cs->irb.GetPointer(cs->irb.GetSAAData(array), cs->irb.ReadPtr(idxptr)); else if(array->getType()->isArraySliceType()) - theptr = cs->irb.PointerAdd(cs->irb.GetArraySliceData(array), cs->irb.ReadPtr(idxptr)); + theptr = cs->irb.GetPointer(cs->irb.GetArraySliceData(array), cs->irb.ReadPtr(idxptr)); else if(array->getType()->isStringType()) - theptr = cs->irb.PointerTypeCast(cs->irb.PointerAdd(cs->irb.GetSAAData(array), cs->irb.ReadPtr(idxptr)), fir::Type::getInt8Ptr()); + theptr = cs->irb.PointerTypeCast(cs->irb.GetPointer(cs->irb.GetSAAData(array), cs->irb.ReadPtr(idxptr)), fir::Type::getInt8Ptr()); else if(array->getType()->isArrayType()) { @@ -237,7 +246,7 @@ CGResult sst::ForeachLoop::_codegen(cgn::CodegenState* cs, fir::Type* inferred) if(array->islorclvalue()) arrptr = cs->irb.AddressOf(array, false); else arrptr = cs->irb.CreateConstLValue(array); - theptr = cs->irb.PointerAdd(cs->irb.ConstGEP2(arrptr, 0, 0), cs->irb.ReadPtr(idxptr)); + theptr = cs->irb.GetPointer(cs->irb.ConstGEP2(arrptr, 0, 0), cs->irb.ReadPtr(idxptr)); } else { @@ -259,7 +268,7 @@ CGResult sst::ForeachLoop::_codegen(cgn::CodegenState* cs, fir::Type* inferred) if(this->indexVar) { - auto idx = new sst::RawValueExpr(this->indexVar->loc, fir::Type::getInt64()); + auto idx = util::pool(this->indexVar->loc, fir::Type::getNativeWord()); idx->rawValue = CGResult(cs->irb.ReadPtr(iterptr)); this->indexVar->init = idx; @@ -274,7 +283,7 @@ CGResult sst::ForeachLoop::_codegen(cgn::CodegenState* cs, fir::Type* inferred) // increment the index cs->irb.WritePtr(cs->irb.Add(cs->irb.ReadPtr(idxptr), step), idxptr); - cs->irb.WritePtr(cs->irb.Add(cs->irb.ReadPtr(iterptr), fir::ConstantInt::getInt64(1)), iterptr); + cs->irb.WritePtr(cs->irb.Add(cs->irb.ReadPtr(iterptr), fir::ConstantInt::getNative(1)), iterptr); cs->irb.UnCondBranch(check); } diff --git a/source/codegen/misc.cpp b/source/codegen/misc.cpp index 81c0ee63..e3d8bd15 100644 --- a/source/codegen/misc.cpp +++ b/source/codegen/misc.cpp @@ -1,5 +1,5 @@ // misc.cpp -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "sst.h" @@ -12,20 +12,37 @@ CGResult sst::TypeExpr::_codegen(cgn::CodegenState* cs, fir::Type* infer) CGResult sst::ScopeExpr::_codegen(cgn::CodegenState* cs, fir::Type* infer) { - error(this, "Failed to resolve scope '%s'", util::serialiseScope(this->scope)); + error(this, "failed to resolve scope '%s'", util::serialiseScope(this->scope)); } CGResult sst::TreeDefn::_codegen(cgn::CodegenState* cs, fir::Type* infer) { - error(this, "Cannot codegen tree definition -- something fucked up somewhere"); + error(this, "cannot codegen tree definition -- something fucked up somewhere"); } +CGResult sst::BareTypeDefn::_codegen(cgn::CodegenState* cs, fir::Type* infer) +{ + // there's nothing to do here... + cs->pushLoc(this); + defer(cs->popLoc()); + return CGResult(0); +} - - - +CGResult sst::Stmt::codegen(cgn::CodegenState* cs, fir::Type* inferred) +{ + if(didCodegen && this->cachedCSId == cs->id) + { + return cachedResult; + } + else + { + this->didCodegen = true; + this->cachedCSId = cs->id; + return (this->cachedResult = this->_codegen(cs, inferred)); + } +} diff --git a/source/codegen/operators.cpp b/source/codegen/operators.cpp index 2e6e7445..80ea503b 100644 --- a/source/codegen/operators.cpp +++ b/source/codegen/operators.cpp @@ -1,5 +1,5 @@ // operators.cpp -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "sst.h" @@ -44,7 +44,7 @@ namespace cgn } else { - error(this->loc(), "Unsupported operator function '%s' on types '%s' and '%s'", op, a, b); + error(this->loc(), "unsupported operator function '%s' on types '%s' and '%s'", op, a, b); } } } diff --git a/source/codegen/ranges.cpp b/source/codegen/ranges.cpp index 5b14f126..9be74c13 100644 --- a/source/codegen/ranges.cpp +++ b/source/codegen/ranges.cpp @@ -1,5 +1,5 @@ // ranges.cpp -// Copyright (c) 2017, zhiayang@gmail.com +// Copyright (c) 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "sst.h" @@ -11,31 +11,31 @@ CGResult sst::RangeExpr::_codegen(cgn::CodegenState* cs, fir::Type* infer) cs->pushLoc(this); defer(cs->popLoc()); - auto start = cs->oneWayAutocast(this->start->codegen(cs, fir::Type::getInt64()).value, fir::Type::getInt64()); + auto start = cs->oneWayAutocast(this->start->codegen(cs, fir::Type::getNativeWord()).value, fir::Type::getNativeWord()); iceAssert(start); if(!start->getType()->isIntegerType()) - error(this->start, "Expected integer type in range expression (start), found '%s' instead", start->getType()); + error(this->start, "expected integer type in range expression (start), found '%s' instead", start->getType()); - auto end = cs->oneWayAutocast(this->end->codegen(cs, fir::Type::getInt64()).value, fir::Type::getInt64()); + auto end = cs->oneWayAutocast(this->end->codegen(cs, fir::Type::getNativeWord()).value, fir::Type::getNativeWord()); iceAssert(end); if(!end->getType()->isIntegerType()) - error(this->end, "Expected integer type in range expression (end), found '%s' instead", end->getType()); + error(this->end, "expected integer type in range expression (end), found '%s' instead", end->getType()); // if we're half-open, then we need to subtract 1 from the end value. // TODO: do we need to check for start > end for half open? // it's well documented that we always subtract 1 for half open, but it might be immediately obvious. - if(this->halfOpen) end = cs->irb.Subtract(end, fir::ConstantInt::getInt64(1)); + if(this->halfOpen) end = cs->irb.Subtract(end, fir::ConstantInt::getNative(1)); // if start > end, the automatic step should be -1. else, it should be 1 as normal. fir::Value* step = (this->step ? - cs->oneWayAutocast(this->step->codegen(cs, fir::Type::getInt64()).value, fir::Type::getInt64()) : - cs->irb.Select(cs->irb.ICmpLEQ(start, end), fir::ConstantInt::getInt64(1), fir::ConstantInt::getInt64(-1)) + cs->oneWayAutocast(this->step->codegen(cs, fir::Type::getNativeWord()).value, fir::Type::getNativeWord()) : + cs->irb.Select(cs->irb.ICmpLEQ(start, end), fir::ConstantInt::getNative(1), fir::ConstantInt::getNative(-1)) ); iceAssert(step); if(!step->getType()->isIntegerType()) - error(this->step, "Expected integer type in range expression (step), found '%s' instead", step->getType()); + error(this->step, "expected integer type in range expression (step), found '%s' instead", step->getType()); auto ret = cs->irb.CreateValue(fir::RangeType::get()); ret = cs->irb.SetRangeLower(ret, start); @@ -44,9 +44,8 @@ CGResult sst::RangeExpr::_codegen(cgn::CodegenState* cs, fir::Type* infer) // now that we have all the values, it's time to sanity check these things. auto checkf = cgn::glue::misc::getRangeSanityCheckFunction(cs); - iceAssert(checkf); + if(checkf) cs->irb.Call(checkf, ret, fir::ConstantString::get(this->loc.toString())); - cs->irb.Call(checkf, ret, fir::ConstantString::get(this->loc.toString())); return CGResult(ret); } diff --git a/source/codegen/refcounting.cpp b/source/codegen/refcounting.cpp index cb6bba89..651d212a 100644 --- a/source/codegen/refcounting.cpp +++ b/source/codegen/refcounting.cpp @@ -1,5 +1,5 @@ // refcounting.cpp -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "sst.h" @@ -14,7 +14,7 @@ namespace cgn list->push_back(v); else - error(l, "Adding duplicate refcounted %s (ptr = %p, type = '%s')", kind, v, v->getType()); + error(l, "adding duplicate refcounted %s (ptr = %p, type = '%s')", kind, v, v->getType()); } static void _removeRC(const Location& l, fir::Value* v, std::vector* list, std::string kind, bool ignore) @@ -23,7 +23,7 @@ namespace cgn list->erase(it); else if(!ignore) - error(l, "Removing non-existent refcounted %s (ptr = %p, type = '%s')", kind, v, v->getType()); + error(l, "removing non-existent refcounted %s (ptr = %p, type = '%s')", kind, v, v->getType()); } diff --git a/source/codegen/sizeof.cpp b/source/codegen/sizeof.cpp index ac772d75..f40844fa 100644 --- a/source/codegen/sizeof.cpp +++ b/source/codegen/sizeof.cpp @@ -1,5 +1,5 @@ // sizeof.cpp -// Copyright (c) 2017, zhiayang@gmail.com +// Copyright (c) 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "sst.h" @@ -10,7 +10,7 @@ CGResult sst::SizeofOp::_codegen(cgn::CodegenState* cs, fir::Type* inferred) cs->pushLoc(this); defer(cs->popLoc()); - auto sz = fir::ConstantInt::getInt64(fir::getSizeOfType(this->typeToSize)); + auto sz = fir::ConstantInt::getNative(fir::getSizeOfType(this->typeToSize)); return CGResult(sz); } @@ -20,6 +20,6 @@ CGResult sst::TypeidOp::_codegen(cgn::CodegenState* cs, fir::Type* inferred) cs->pushLoc(this); defer(cs->popLoc()); - auto sz = fir::ConstantInt::getUint64(this->typeToId->getID()); + auto sz = fir::ConstantInt::getUNative(this->typeToId->getID()); return CGResult(sz); } diff --git a/source/codegen/slice.cpp b/source/codegen/slice.cpp index 77a7be45..9f311ba0 100644 --- a/source/codegen/slice.cpp +++ b/source/codegen/slice.cpp @@ -1,11 +1,12 @@ // slice.cpp -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "sst.h" #include "codegen.h" #include "platform.h" #include "gluecode.h" +#include "mpool.h" static void checkSliceOperation(cgn::CodegenState* cs, sst::Expr* user, fir::Value* maxlen, fir::Value* beginIndex, fir::Value* endIndex, sst::Expr* bexpr, sst::Expr* eexpr) @@ -32,13 +33,13 @@ static void checkSliceOperation(cgn::CodegenState* cs, sst::Expr* user, fir::Val auto merge = cs->irb.addNewBlockInFunction("merge", cs->irb.getCurrentFunction()); { - fir::Value* neg = cs->irb.ICmpLT(beginIndex, fir::ConstantInt::getInt64(0)); + fir::Value* neg = cs->irb.ICmpLT(beginIndex, fir::ConstantInt::getNative(0)); cs->irb.CondBranch(neg, neg_begin, check1); } cs->irb.setCurrentBlock(check1); { - fir::Value* neg = cs->irb.ICmpLT(endIndex, fir::ConstantInt::getInt64(0)); + fir::Value* neg = cs->irb.ICmpLT(endIndex, fir::ConstantInt::getNative(0)); cs->irb.CondBranch(neg, neg_end, check2); } @@ -69,9 +70,8 @@ static void checkSliceOperation(cgn::CodegenState* cs, sst::Expr* user, fir::Val // endindex is non-inclusive -- if we're doing a decomposition check then it compares length instead // of indices here. fir::Function* checkf = cgn::glue::array::getBoundsCheckFunction(cs, /* isPerformingDecomposition: */ true); - iceAssert(checkf); - - cs->irb.Call(checkf, maxlen, endIndex, fir::ConstantString::get(apos.toString())); + if(checkf) + cs->irb.Call(checkf, maxlen, endIndex, fir::ConstantString::get(apos.toString())); } } @@ -92,7 +92,7 @@ static CGResult performSliceOperation(cgn::CodegenState* cs, sst::SliceOp* user, // FINALLY. // increment ptr - fir::Value* newptr = cs->irb.PointerAdd(data, beginIndex, "newptr"); + fir::Value* newptr = cs->irb.GetPointer(data, beginIndex, "newptr"); fir::Value* newlen = cs->irb.Subtract(endIndex, beginIndex, "newlen"); slice = cs->irb.SetArraySliceData(slice, newptr); @@ -122,8 +122,8 @@ CGResult sst::SliceOp::_codegen(cgn::CodegenState* cs, fir::Type* infer) if(ty->isDynamicArrayType()) length = cs->irb.GetSAALength(lhs, "orig_len"); else if(ty->isArraySliceType()) length = cs->irb.GetArraySliceLength(lhs, "orig_len"); else if(ty->isStringType()) length = cs->irb.GetSAALength(lhs, "orig_len"); - else if(ty->isArrayType()) length = fir::ConstantInt::getInt64(ty->toArrayType()->getArraySize()); - else if(ty->isPointerType()) length = fir::ConstantInt::getInt64(0); + else if(ty->isArrayType()) length = fir::ConstantInt::getNative(ty->toArrayType()->getArraySize()); + else if(ty->isPointerType()) length = fir::ConstantInt::getNative(0); else error(this, "unsupported type '%s'", ty); fir::Value* beginIdx = 0; @@ -139,13 +139,13 @@ CGResult sst::SliceOp::_codegen(cgn::CodegenState* cs, fir::Type* infer) error(this, "slicing operation on pointers requires an ending index"); if(this->begin) beginIdx = this->begin->codegen(cs).value; - else beginIdx = fir::ConstantInt::getInt64(0); + else beginIdx = fir::ConstantInt::getNative(0); if(this->end) endIdx = this->end->codegen(cs).value; else endIdx = length; - beginIdx = cs->oneWayAutocast(beginIdx, fir::Type::getInt64()); - endIdx = cs->oneWayAutocast(endIdx, fir::Type::getInt64()); + beginIdx = cs->oneWayAutocast(beginIdx, fir::Type::getNativeWord()); + endIdx = cs->oneWayAutocast(endIdx, fir::Type::getNativeWord()); } beginIdx->setName("begin"); @@ -241,7 +241,7 @@ CGResult sst::SplatExpr::_codegen(cgn::CodegenState* cs, fir::Type* infer) { // just do a slice on it. auto target = fir::ArraySliceType::getVariadic(ty->getArrayElementType()); - auto slice = new sst::SliceOp(this->loc, target); + auto slice = util::pool(this->loc, target); slice->expr = this->inside; slice->begin = 0; diff --git a/source/codegen/structs.cpp b/source/codegen/structs.cpp index 1af5ff98..b1b8bc3b 100644 --- a/source/codegen/structs.cpp +++ b/source/codegen/structs.cpp @@ -1,5 +1,5 @@ // structs.cpp -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "errors.h" diff --git a/source/codegen/subscript.cpp b/source/codegen/subscript.cpp index c59fdce4..213ca26d 100644 --- a/source/codegen/subscript.cpp +++ b/source/codegen/subscript.cpp @@ -1,5 +1,5 @@ // subscript.cpp -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "sst.h" @@ -15,8 +15,6 @@ CGResult sst::SubscriptOp::_codegen(cgn::CodegenState* cs, fir::Type* infer) auto lr = this->expr->codegen(cs); auto lt = lr.value->getType(); - fir::Function* boundscheckfn = cgn::glue::saa_common::generateBoundsCheckFunction(cs, - /* isString: */ lt->isStringType(), /* isDecomp: */false);; fir::Value* datapointer = 0; fir::Value* maxlength = 0; @@ -36,9 +34,9 @@ CGResult sst::SubscriptOp::_codegen(cgn::CodegenState* cs, fir::Type* infer) // TODO: LVALUE HOLE if(lr->islorclvalue()) { - datapointer = cs->irb.GEP2(cs->irb.AddressOf(lr.value, true), fir::ConstantInt::getInt64(0), - fir::ConstantInt::getInt64(0)); - maxlength = fir::ConstantInt::getInt64(lt->toArrayType()->getArraySize()); + datapointer = cs->irb.GEP2(cs->irb.AddressOf(lr.value, true), fir::ConstantInt::getNative(0), + fir::ConstantInt::getNative(0)); + maxlength = fir::ConstantInt::getNative(lt->toArrayType()->getArraySize()); } else { @@ -72,7 +70,11 @@ CGResult sst::SubscriptOp::_codegen(cgn::CodegenState* cs, fir::Type* infer) } if(maxlength) - cs->irb.Call(boundscheckfn, maxlength, index, fir::ConstantString::get(this->loc.shortString())); + { + fir::Function* checkf = cgn::glue::saa_common::generateBoundsCheckFunction(cs, /* isString: */ lt->isStringType(), /* isDecomp: */false); + if(checkf) + cs->irb.Call(checkf, maxlength, index, fir::ConstantString::get(this->loc.shortString())); + } // ok, do it fir::Value* ptr = cs->irb.GetPointer(datapointer, index); diff --git a/source/codegen/toplevel.cpp b/source/codegen/toplevel.cpp index e7cee4fc..691aabb2 100644 --- a/source/codegen/toplevel.cpp +++ b/source/codegen/toplevel.cpp @@ -1,5 +1,5 @@ // toplevel.cpp -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "sst.h" @@ -9,36 +9,41 @@ #include "ir/type.h" #include "ir/module.h" +#include "ir/interp.h" #include "ir/irbuilder.h" +#include "mpool.h" + namespace cgn { - fir::Module* codegen(sst::DefinitionTree* dtr) + static size_t csid = 0; + CodegenState::CodegenState(const fir::IRBuilder& i) : irb(i) { - // debuglog("codegen for %s\n", dtr->base->name.c_str()); + this->id = csid++; + } + fir::Module* codegen(sst::DefinitionTree* dtr) + { auto mod = new fir::Module(dtr->base->name); auto builder = fir::IRBuilder(mod); auto cs = new CodegenState(builder); - cs->stree = dtr->base; cs->module = mod; cs->typeDefnMap = dtr->typeDefnMap; - // cs->vtree = new ValueTree(dtr->base->name, 0); - - cs->pushLoc(dtr->topLevel); - defer(cs->popLoc()); - - dtr->topLevel->codegen(cs); + { + cs->pushLoc(dtr->topLevel); + defer(cs->popLoc()); - cs->finishGlobalInitFunction(); + dtr->topLevel->codegen(cs); + cs->finishGlobalInitFunction(); + } - // debuglog("\n\n\n%s\n\n", cs->module->print().c_str()); mod->setEntryFunction(cs->entryFunction.first); - return cs->module; + delete cs; + return mod; } } diff --git a/source/codegen/unions.cpp b/source/codegen/unions.cpp index c84d7fda..ca43dbdc 100644 --- a/source/codegen/unions.cpp +++ b/source/codegen/unions.cpp @@ -1,5 +1,5 @@ // unions.cpp -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "sst.h" diff --git a/source/codegen/variable.cpp b/source/codegen/variable.cpp index a0510c86..1fbc07e7 100644 --- a/source/codegen/variable.cpp +++ b/source/codegen/variable.cpp @@ -1,5 +1,5 @@ // variable.cpp -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "sst.h" @@ -21,7 +21,7 @@ CGResult sst::VarDefn::_codegen(cgn::CodegenState* cs, fir::Type* infer) { iceAssert(this->init); - SpanError::make(SimpleError::make(this->loc, "Cannot initialise variable of type '%s' with a value of type '%s'", this->type, nv->getType())) + SpanError::make(SimpleError::make(this->loc, "cannot initialise variable of type '%s' with a value of type '%s'", this->type, nv->getType())) ->add(util::ESpan(this->init->loc, strprintf("type '%s'", nv->getType()))) ->postAndQuit(); } @@ -39,66 +39,67 @@ CGResult sst::VarDefn::_codegen(cgn::CodegenState* cs, fir::Type* infer) // else fir::Value* res = 0; - if(this->init) - res = this->init->codegen(cs, this->type).value; - - else - res = cs->getDefaultValue(this->type); - - res = checkStore(res); + if(this->init) res = this->init->codegen(cs, this->type).value; + else res = cs->getDefaultValue(this->type); //* note: we declare it as not-immutable here to make it easier to set things, but otherwise we make it immutable again below after init. - auto alloc = cs->module->createGlobalVariable(this->id, this->type, false, this->visibility == VisibilityLevel::Public ? fir::LinkageType::External : fir::LinkageType::Internal); + auto glob = cs->module->createGlobalVariable(this->id, this->type, false, + this->visibility == VisibilityLevel::Public ? fir::LinkageType::External : fir::LinkageType::Internal); - cs->autoAssignRefCountedValue(alloc, res, true, true); + if(auto cv = dcast(fir::ConstantValue, res); cv && cv->getType() == this->type) + { + glob->setInitialValue(cv); + } + else + { + res = checkStore(res); + cs->autoAssignRefCountedValue(glob, res, true, true); + } // go and fix the thing. if(this->immutable) - alloc->makeConst(); + glob->makeConst(); cs->leaveGlobalInitFunction(rest); - cs->valueMap[this] = CGResult(alloc); - return CGResult(alloc); + cs->valueMap[this] = CGResult(glob); + return CGResult(glob); } + else + { + fir::Value* val = 0; + fir::Value* alloc = 0; + fir::Value* res = 0; + if(this->init) + { + res = this->init->codegen(cs, this->type).value; + res = cs->oneWayAutocast(res, this->type); + val = res; + } + if(!val) val = cs->getDefaultValue(this->type); + val = checkStore(val); - fir::Value* val = 0; - fir::Value* alloc = 0; - - fir::Value* res = 0; - if(this->init) - { - res = this->init->codegen(cs, this->type).value; - res = cs->oneWayAutocast(res, this->type); - - val = res; - } - - if(!val) val = cs->getDefaultValue(this->type); + if(this->immutable) + { + iceAssert(val); + alloc = cs->irb.CreateConstLValue(val, this->id.name); + } + else + { + alloc = cs->irb.CreateLValue(this->type, this->id.name); + } + iceAssert(alloc); - val = checkStore(val); + cs->addVariableUsingStorage(this, alloc, CGResult(res)); - if(this->immutable) - { - iceAssert(val); - alloc = cs->irb.CreateConstLValue(val, this->id.name); - } - else - { - alloc = cs->irb.CreateLValue(this->type, this->id.name); + return CGResult(alloc); } - - iceAssert(alloc); - - cs->addVariableUsingStorage(this, alloc, CGResult(res)); - - return CGResult(alloc); } void cgn::CodegenState::addVariableUsingStorage(sst::VarDefn* var, fir::Value* alloc, CGResult val) @@ -149,8 +150,8 @@ CGResult sst::VarRef::_codegen(cgn::CodegenState* cs, fir::Type* infer) it = cs->valueMap.find(this->def); if(it == cs->valueMap.end()) { - SimpleError::make(this->loc, "Failed to codegen variable definition for '%s'", this->name) - ->append(SimpleError::make(MsgType::Note, this->def->loc, "Offending definition is here:")) + SimpleError::make(this->loc, "failed to codegen variable definition for '%s'", this->name) + ->append(SimpleError::make(MsgType::Note, this->def->loc, "offending definition is here:")) ->postAndQuit(); } @@ -161,7 +162,7 @@ CGResult sst::VarRef::_codegen(cgn::CodegenState* cs, fir::Type* infer) // make sure types match... should we bother? if(value->getType() != this->type) - error(this, "Type mismatch; typechecking found type '%s', codegen gave type '%s'", this->type, value->getType()); + error(this, "type mismatch; typechecking found type '%s', codegen gave type '%s'", this->type, value->getType()); return CGResult(value); } diff --git a/source/fir/ConstantValue.cpp b/source/fir/ConstantValue.cpp index 341a73b3..a99f6733 100644 --- a/source/fir/ConstantValue.cpp +++ b/source/fir/ConstantValue.cpp @@ -1,5 +1,5 @@ // ConstantValue.cpp -// Copyright (c) 2014 - 2016, zhiayang@gmail.com +// Copyright (c) 2014 - 2016, zhiayang // Licensed under the Apache License Version 2.0. #include "ir/value.h" @@ -135,6 +135,16 @@ namespace fir return ConstantInt::get(Type::getUint64(), value); } + ConstantInt* ConstantInt::getNative(int64_t value) + { + return ConstantInt::get(Type::getNativeWord(), value); + } + + ConstantInt* ConstantInt::getUNative(uint64_t value) + { + return ConstantInt::get(Type::getNativeUWord(), value); + } + @@ -213,13 +223,13 @@ namespace fir ConstantStruct::ConstantStruct(StructType* st, std::vector members) : ConstantValue(st) { if(st->getElementCount() != members.size()) - error("Mismatched structs: expected %zu fields, got %zu", st->getElementCount(), members.size()); + error("mismatched structs: expected %zu fields, got %zu", st->getElementCount(), members.size()); for(size_t i = 0; i < st->getElementCount(); i++) { if(st->getElementN(i) != members[i]->getType()) { - error("Mismatched types in field %zu: expected '%s', got '%s'", i, st->getElementN(i), members[i]->getType()); + error("mismatched types in field %zu: expected '%s', got '%s'", i, st->getElementN(i), members[i]->getType()); } } diff --git a/source/fir/Function.cpp b/source/fir/Function.cpp index 44c9002d..7f41cf3b 100644 --- a/source/fir/Function.cpp +++ b/source/fir/Function.cpp @@ -1,5 +1,5 @@ // Function.cpp -// Copyright (c) 2014 - 2016, zhiayang@gmail.com +// Copyright (c) 2014 - 2016, zhiayang // Licensed under the Apache License Version 2.0. #include "ir/module.h" @@ -23,7 +23,7 @@ namespace fir Value* Argument::getActualValue() { if(this->realValue) return this->realValue; - error("Calling getActualValue() when not in function! (no real value)"); + error("calling getActualValue() when not in function! (no real value)"); } Function* Argument::getParentFunction() @@ -89,7 +89,7 @@ namespace fir return a; } - error("No argument named '%s' in function '%s'", name, this->getName().name); + error("no argument named '%s' in function '%s'", name, this->getName().name); } bool Function::isCStyleVarArg() @@ -113,6 +113,25 @@ namespace fir this->blocks.clear(); } + bool Function::isIntrinsicFunction() + { + return this->fnIsIntrinsicFunction; + } + + void Function::setIsIntrinsic() + { + this->fnIsIntrinsicFunction = true; + } + + void Function::addStackAllocation(Type* ty) + { + this->stackAllocs.push_back(ty); + } + + std::vector Function::getStackAllocations() + { + return this->stackAllocs; + } void Function::cullUnusedValues() @@ -168,7 +187,7 @@ namespace fir FunctionType* Function::getType() { FunctionType* ft = dcast(FunctionType, this->valueType); - iceAssert(ft && "Function is impostor (not valid function type)"); + iceAssert(ft && "function is impostor (not valid function type)"); return ft; } diff --git a/source/fir/GlobalValue.cpp b/source/fir/GlobalValue.cpp index 136e6d2d..7df747ff 100644 --- a/source/fir/GlobalValue.cpp +++ b/source/fir/GlobalValue.cpp @@ -1,5 +1,5 @@ // GlobalValue.cpp -// Copyright (c) 2014 - 2016, zhiayang@gmail.com +// Copyright (c) 2014 - 2016, zhiayang // Licensed under the Apache License Version 2.0. @@ -35,10 +35,9 @@ namespace fir void GlobalVariable::setInitialValue(ConstantValue* constVal) { - if(constVal && constVal->getType() != this->getType()->getPointerElementType()) + if(constVal && constVal->getType() != this->getType()) error("storing value of '%s' in global var of type '%s'", constVal->getType(), this->getType()); - iceAssert((!constVal || constVal->getType() == this->getType()->getPointerElementType()) && "invalid type"); this->initValue = constVal; } diff --git a/source/fir/IRBlock.cpp b/source/fir/IRBlock.cpp index d2ab77a7..728a38d9 100644 --- a/source/fir/IRBlock.cpp +++ b/source/fir/IRBlock.cpp @@ -1,5 +1,5 @@ // IRBlock.cpp -// Copyright (c) 2014 - 2016, zhiayang@gmail.com +// Copyright (c) 2014 - 2016, zhiayang // Licensed under the Apache License Version 2.0. #include "ir/function.h" diff --git a/source/fir/IRBuilder.cpp b/source/fir/IRBuilder.cpp index cd076681..b376cc9e 100644 --- a/source/fir/IRBuilder.cpp +++ b/source/fir/IRBuilder.cpp @@ -1,5 +1,5 @@ // IRBuilder.cpp -// Copyright (c) 2014 - 2016, zhiayang@gmail.com +// Copyright (c) 2014 - 2016, zhiayang // Licensed under the Apache License Version 2.0. #include @@ -354,7 +354,7 @@ namespace fir Value* IRBuilder::Add(Value* a, Value* b, const std::string& vname) { if(a->getType() != b->getType()) - error("creating add instruction with non-equal types ('%s' vs '%s')", a->getType(), b->getType()); + error("irbuilder: creating add instruction with non-equal types ('%s' vs '%s')", a->getType(), b->getType()); OpKind ok = OpKind::Invalid; if(a->getType()->isSignedIntType()) ok = OpKind::Signed_Add; @@ -369,7 +369,7 @@ namespace fir Value* IRBuilder::Subtract(Value* a, Value* b, const std::string& vname) { if(a->getType() != b->getType()) - error("creating sub instruction with non-equal types ('%s' vs '%s')", a->getType(), b->getType()); + error("irbuilder: creating sub instruction with non-equal types ('%s' vs '%s')", a->getType(), b->getType()); OpKind ok = OpKind::Invalid; if(a->getType()->isSignedIntType()) ok = OpKind::Signed_Sub; @@ -383,7 +383,7 @@ namespace fir Value* IRBuilder::Multiply(Value* a, Value* b, const std::string& vname) { if(a->getType() != b->getType()) - error("creating mul instruction with non-equal types ('%s' vs '%s')", a->getType(), b->getType()); + error("irbuilder: creating mul instruction with non-equal types ('%s' vs '%s')", a->getType(), b->getType()); OpKind ok = OpKind::Invalid; if(a->getType()->isSignedIntType()) ok = OpKind::Signed_Mul; @@ -397,7 +397,7 @@ namespace fir Value* IRBuilder::Divide(Value* a, Value* b, const std::string& vname) { if(a->getType() != b->getType()) - error("creating div instruction with non-equal types ('%s' vs '%s')", a->getType(), b->getType()); + error("irbuilder: creating div instruction with non-equal types ('%s' vs '%s')", a->getType(), b->getType()); OpKind ok = OpKind::Invalid; @@ -412,7 +412,7 @@ namespace fir Value* IRBuilder::Modulo(Value* a, Value* b, const std::string& vname) { if(a->getType() != b->getType()) - error("creating mod instruction with non-equal types ('%s' vs '%s')", a->getType(), b->getType()); + error("irbuilder: creating mod instruction with non-equal types ('%s' vs '%s')", a->getType(), b->getType()); OpKind ok = OpKind::Invalid; if(a->getType()->isSignedIntType()) ok = OpKind::Signed_Mod; @@ -451,7 +451,7 @@ namespace fir if(a->getType() != b->getType() && !(a->getType()->isPointerType() && b->getType()->isPointerType() && a->getType()->getPointerElementType() == b->getType()->getPointerElementType())) { - error("creating icmp eq instruction with non-equal types"); + error("irbuilder: creating icmp eq instruction with non-equal types"); } Instruction* instr = make_instr(OpKind::ICompare_Equal, false, this->currentBlock, fir::Type::getBool(), @@ -465,7 +465,7 @@ namespace fir if(a->getType() != b->getType() && !(a->getType()->isPointerType() && b->getType()->isPointerType() && a->getType()->getPointerElementType() == b->getType()->getPointerElementType())) { - error("creating icmp neq instruction with non-equal types"); + error("irbuilder: creating icmp neq instruction with non-equal types"); } Instruction* instr = make_instr(OpKind::ICompare_NotEqual, false, this->currentBlock, fir::Type::getBool(), @@ -479,7 +479,7 @@ namespace fir if(a->getType() != b->getType() && !(a->getType()->isPointerType() && b->getType()->isPointerType() && a->getType()->getPointerElementType() == b->getType()->getPointerElementType())) { - error("creating icmp gt instruction with non-equal types"); + error("irbuilder: creating icmp gt instruction with non-equal types"); } Instruction* instr = make_instr(OpKind::ICompare_Greater, false, this->currentBlock, fir::Type::getBool(), @@ -493,7 +493,7 @@ namespace fir if(a->getType() != b->getType() && !(a->getType()->isPointerType() && b->getType()->isPointerType() && a->getType()->getPointerElementType() == b->getType()->getPointerElementType())) { - error("creating icmp lt instruction with non-equal types"); + error("irbuilder: creating icmp lt instruction with non-equal types"); } Instruction* instr = make_instr(OpKind::ICompare_Less, false, this->currentBlock, fir::Type::getBool(), @@ -507,7 +507,7 @@ namespace fir if(a->getType() != b->getType() && !(a->getType()->isPointerType() && b->getType()->isPointerType() && a->getType()->getPointerElementType() == b->getType()->getPointerElementType())) { - error("creating icmp geq instruction with non-equal types"); + error("irbuilder: creating icmp geq instruction with non-equal types"); } Instruction* instr = make_instr(OpKind::ICompare_GreaterEqual, false, this->currentBlock, fir::Type::getBool(), @@ -521,7 +521,7 @@ namespace fir if(a->getType() != b->getType() && !(a->getType()->isPointerType() && b->getType()->isPointerType() && a->getType()->getPointerElementType() == b->getType()->getPointerElementType())) { - error("creating icmp leq instruction with non-equal types"); + error("irbuilder: creating icmp leq instruction with non-equal types"); } Instruction* instr = make_instr(OpKind::ICompare_LessEqual, false, this->currentBlock, fir::Type::getBool(), @@ -648,7 +648,7 @@ namespace fir iceAssert(a->getType() == b->getType() && "creating icmp multi instruction with non-equal types"); // iceAssert(a->getType()->isIntegerType() && "creating icmp multi instruction with non-integer type"); Instruction* instr = make_instr(OpKind::ICompare_Multi, false, this->currentBlock, - fir::Type::getInt64(), { a, b }); + fir::Type::getNativeWord(), { a, b }); return this->addInstruction(instr, vname); } @@ -657,7 +657,7 @@ namespace fir iceAssert(a->getType() == b->getType() && "creating cmp leq instruction with non-equal types"); iceAssert(a->getType()->isFloatingPointType() && "creating fcmp instruction with non floating-point types"); Instruction* instr = make_instr(OpKind::FCompare_Multi, false, this->currentBlock, - fir::Type::getInt64(), { a, b }); + fir::Type::getNativeWord(), { a, b }); return this->addInstruction(instr, vname); } @@ -774,7 +774,7 @@ namespace fir double _ = 0; if(std::modf(cfp->getValue(), &_) != 0.0) - warn("Truncating constant '%Lf' in constant cast to type '%s'", cfp->getValue(), targetType); + warn("truncating constant '%Lf' in constant cast to type '%s'", cfp->getValue(), targetType); return ConstantInt::get(targetType, (size_t) cfp->getValue()); } @@ -807,7 +807,7 @@ namespace fir } else { - error("Unknown floating point type '%s'", targetType); + error("irbuilder: unknown floating point type '%s'", targetType); } return ret; @@ -863,7 +863,7 @@ namespace fir Value* IRBuilder::IntZeroExt(Value* v, Type* targetType, const std::string& vname) { - iceAssert(v->getType()->isIntegerType() && "value is not integer type"); + iceAssert((v->getType()->isIntegerType() || v->getType()->isBoolType()) && "value is not integer type"); iceAssert(targetType->isIntegerType() && "target is not integer type"); Instruction* instr = make_instr(OpKind::Integer_ZeroExt, false, this->currentBlock, targetType, @@ -939,7 +939,7 @@ namespace fir { if(args.size() != fn->getArgumentCount() && !fn->isVariadic() && !fn->isCStyleVarArg()) { - error("Calling function '%s' with the wrong number of arguments (needs %zu, have %zu)", fn->getName().str(), + error("irbuilder: calling function '%s' with the wrong number of arguments (needs %zu, have %zu)", fn->getName().str(), fn->getArgumentCount(), args.size()); } @@ -989,7 +989,7 @@ namespace fir if(out[i]->getType() != target) { - error("Mismatch in argument type (arg. %zu) in function '%s' (need '%s', have '%s')", i, fn->getName().str(), + error("irbuilder: mismatch in argument type (arg. %zu) in function '%s' (need '%s', have '%s')", i, fn->getName().str(), fn->getArguments()[i]->getType(), out[i]->getType()); } } @@ -1005,7 +1005,7 @@ namespace fir } else if(args[i]->getType() != elm) { - error("Mismatch in argument type (in variadic portion) (arg. %zu) in function '%s' (need '%s', have '%s')", i, fn->getName().str(), + error("irbuilder: mismatch in argument type (in variadic portion) (arg. %zu) in function '%s' (need '%s', have '%s')", i, fn->getName().str(), elm, args[i]->getType()); } else @@ -1054,7 +1054,7 @@ namespace fir // ugh, fix mutability cast. slc = this->SetArraySliceData(slc, this->PointerTypeCast(this->ConstGEP2(arrptr, 0, 0), elm->getPointerTo())); - slc = this->SetArraySliceLength(slc, fir::ConstantInt::getInt64(variadicArgs.size())); + slc = this->SetArraySliceLength(slc, fir::ConstantInt::getNative(variadicArgs.size())); // ok, this is the last argument. out.push_back(slc); @@ -1066,7 +1066,7 @@ namespace fir // { // // check here, to stop llvm dying // if(args.size() != fn->getArgumentCount()) - // error("Calling function '%s' with the wrong number of arguments (needs %zu, have %zu)", fn->getName().str(), + // error("irbuilder: calling function '%s' with the wrong number of arguments (needs %zu, have %zu)", fn->getName().str(), // fn->getArgumentCount(), args.size()); // for(size_t i = 0; i < args.size(); i++) @@ -1092,7 +1092,7 @@ namespace fir // out[i] = args[i]; // if(out[i]->getType() != target) // { - // error("Mismatch in argument type (arg. %zu) in function '%s' (need '%s', have '%s')", i, fn->getName().str(), + // error("irbuilder: mismatch in argument type (arg. %zu) in function '%s' (need '%s', have '%s')", i, fn->getName().str(), // fn->getArguments()[i]->getType(), out[i]->getType()); // } // } @@ -1132,7 +1132,7 @@ namespace fir iceAssert(self && self == cls); Instruction* instr = make_instr(OpKind::Value_CallVirtualMethod, true, this->currentBlock, ft->getReturnType(), - (Value*) ConstantValue::getZeroValue(cls) + ((Value*) ConstantInt::getInt64(index) + ((Value*) ConstantValue::getZeroValue(ft) + args))); + (Value*) ConstantValue::getZeroValue(cls) + ((Value*) ConstantInt::getNative(index) + ((Value*) ConstantValue::getZeroValue(ft) + args))); return this->addInstruction(instr, vname); } @@ -1210,6 +1210,9 @@ namespace fir auto parent = this->currentBlock->getParentFunction(); iceAssert(parent); + parent->addStackAllocation(type); + + // get the entry block auto entry = parent->getBlockList().front(); iceAssert(entry); @@ -1234,7 +1237,7 @@ namespace fir Value* IRBuilder::CreateSliceFromSAA(Value* saa, bool mut, const std::string& vname) { if(!isSAAType(saa->getType())) - error("expected string or dynamic array type, found '%s' instead", saa->getType()); + error("irbuilder: expected string or dynamic array type, found '%s' instead", saa->getType()); auto slc = this->CreateValue(saa->getType()->isStringType() ? fir::Type::getCharSlice(mut) : fir::ArraySliceType::get(saa->getType()->getArrayElementType(), mut)); @@ -1268,10 +1271,10 @@ namespace fir Value* IRBuilder::GetRawUnionFieldByType(Value* lval, Type* type, const std::string& vname) { if(!lval->islorclvalue()) - error("cannot do raw union ops on non-lvalue"); + error("irbuilder: cannot do raw union ops on non-lvalue"); if(!lval->getType()->isRawUnionType()) - error("'%s' is not a raw union type!", lval->getType()); + error("irbuilder: '%s' is not a raw union type!", lval->getType()); Instruction* instr = make_instr(OpKind::RawUnion_GEP, false, this->currentBlock, type, { lval, ConstantValue::getZeroValue(type) }); @@ -1284,14 +1287,14 @@ namespace fir Value* IRBuilder::GetRawUnionField(Value* lval, const std::string& field, const std::string& vname) { if(!lval->islorclvalue()) - error("cannot do raw union ops on non-lvalue"); + error("irbuilder: cannot do raw union ops on non-lvalue"); if(!lval->getType()->isRawUnionType()) - error("'%s' is not a raw union type!", lval->getType()); + error("irbuilder: '%s' is not a raw union type!", lval->getType()); auto rut = lval->getType()->toRawUnionType(); if(!rut->hasVariant(field)) - error("union '%s' does not have a field '%s'", rut->getTypeName(), field); + error("irbuilder: union '%s' does not have a field '%s'", rut->getTypeName(), field); auto ty = rut->getVariant(field); return this->GetRawUnionFieldByType(lval, ty, vname); @@ -1303,12 +1306,12 @@ namespace fir static Instruction* doGEPOnCompoundType(IRBlock* parent, T* type, Value* structPtr, size_t memberIndex) { if(!structPtr->islorclvalue()) - error("cannot do GEP on non-lvalue"); + error("irbuilder: cannot do GEP on non-lvalue"); iceAssert(type->getElementCount() > memberIndex && "struct does not have so many members"); Instruction* instr = make_instr(OpKind::Value_GetStructMember, false, parent, type->getElementN(memberIndex), - { structPtr, ConstantInt::getUint64(memberIndex) }, Value::Kind::lvalue); + { structPtr, ConstantInt::getUNative(memberIndex) }, Value::Kind::lvalue); return instr; } @@ -1319,7 +1322,7 @@ namespace fir Value* IRBuilder::StructGEP(Value* structPtr, size_t memberIndex, const std::string& vname) { if(!structPtr->islorclvalue()) - error("cannot do GEP on non-lvalue"); + error("irbuilder: cannot do GEP on non-lvalue"); //* note: we do not allow raw gep (by index) into classes, because V T A B L E if(structPtr->getType()->isStructType()) @@ -1334,14 +1337,14 @@ namespace fir } else { - error("type '%s' is not a valid type to GEP into", structPtr->getType()); + error("irbuilder: type '%s' is not a valid type to GEP into", structPtr->getType()); } } Value* IRBuilder::GetStructMember(Value* ptr, const std::string& memberName) { if(!ptr->islorclvalue()) - error("cannot do GEP on non-lvalue"); + error("irbuilder: cannot do GEP on non-lvalue"); if(ptr->getType()->isStructType()) { @@ -1351,7 +1354,7 @@ namespace fir iceAssert(st->hasElementWithName(memberName) && "no element with such name"); Instruction* instr = make_instr(OpKind::Value_GetStructMember, false, this->currentBlock, - memt, { ptr, ConstantInt::getUint64(st->getElementIndex(memberName)) }, Value::Kind::lvalue); + memt, { ptr, ConstantInt::getUNative(st->getElementIndex(memberName)) }, Value::Kind::lvalue); return this->addInstruction(instr, memberName); } @@ -1364,13 +1367,13 @@ namespace fir //! '+1' is for vtable. Instruction* instr = make_instr(OpKind::Value_GetStructMember, false, this->currentBlock, - memt, { ptr, ConstantInt::getUint64(ct->getElementIndex(memberName) + 1) }, Value::Kind::lvalue); + memt, { ptr, ConstantInt::getUNative(ct->getElementIndex(memberName) + 1) }, Value::Kind::lvalue); return this->addInstruction(instr, memberName); } else { - error("type '%s' is not a valid type to GEP into", ptr->getType()); + error("irbuilder: type '%s' is not a valid type to GEP into", ptr->getType()); } } @@ -1379,14 +1382,14 @@ namespace fir void IRBuilder::SetVtable(Value* ptr, Value* table, const std::string& vname) { if(!ptr->islorclvalue()) - error("cannot do set vtable on non-lvalue"); + error("irbuilder: cannot do set vtable on non-lvalue"); auto ty = ptr->getType(); - if(!ty->isClassType()) error("'%s' is not a class type", ty); - if(table->getType() != fir::Type::getInt8Ptr()) error("expected i8* for vtable, got '%s'", table->getType()); + if(!ty->isClassType()) error("irbuilder: '%s' is not a class type", ty); + if(table->getType() != fir::Type::getInt8Ptr()) error("irbuilder: expected i8* for vtable, got '%s'", table->getType()); Instruction* instr = make_instr(OpKind::Value_GetStructMember, false, this->currentBlock, - fir::Type::getInt8Ptr(), { ptr, ConstantInt::getUint64(0) }, Value::Kind::lvalue); + fir::Type::getInt8Ptr(), { ptr, ConstantInt::getUNative(0) }, Value::Kind::lvalue); auto gep = this->addInstruction(instr, vname); this->Store(table, gep); @@ -1399,10 +1402,10 @@ namespace fir Value* IRBuilder::ConstGEP2(Value* ptr, size_t ptrIndex, size_t elmIndex, const std::string& vname) { if(!ptr->getType()->isPointerType()) - error("ptr is not a pointer type (got '%s')", ptr->getType()); + error("irbuilder: ptr is not a pointer type (got '%s')", ptr->getType()); - auto ptri = ConstantInt::getUint64(ptrIndex); - auto elmi = ConstantInt::getUint64(elmIndex); + auto ptri = ConstantInt::getUNative(ptrIndex); + auto elmi = ConstantInt::getUNative(elmIndex); return this->GEP2(ptr, ptri, elmi); } @@ -1411,10 +1414,10 @@ namespace fir Value* IRBuilder::GEP2(Value* ptr, Value* ptrIndex, Value* elmIndex, const std::string& vname) { if(!ptr->getType()->isPointerType()) - error("ptr is not a pointer type (got '%s')", ptr->getType()); + error("irbuilder: ptr is not a pointer type (got '%s')", ptr->getType()); else if(ptr->getType()->getPointerElementType()->isClassType() || ptr->getType()->getPointerElementType()->isStructType()) - error("use the other function for struct types"); + error("irbuilder: use the other function for struct types"); iceAssert(ptrIndex->getType()->isIntegerType() && "ptrIndex is not integer type"); iceAssert(elmIndex->getType()->isIntegerType() && "elmIndex is not integer type"); @@ -1423,7 +1426,6 @@ namespace fir if(retType->isArrayType()) retType = retType->toArrayType()->getElementType()->getPointerTo(); - if(ptr->getType()->isMutablePointer()) retType = retType->getMutablePointerVersion(); @@ -1436,13 +1438,13 @@ namespace fir Value* IRBuilder::GetPointer(Value* ptr, Value* ptrIndex, const std::string& vname) { if(!ptr->getType()->isPointerType()) - error("ptr is not a pointer type (got '%s')", ptr->getType()); + error("irbuilder: ptr is not a pointer type (got '%s')", ptr->getType()); if(!ptrIndex->getType()->isIntegerType()) - error("ptrIndex is not an integer type (got '%s')", ptrIndex->getType()); + error("irbuilder: ptrIndex is not an integer type (got '%s')", ptrIndex->getType()); if(ptr->getType()->getPointerElementType()->isClassType() || ptr->getType()->getPointerElementType()->isStructType()) - error("use the other function for struct types"); + error("irbuilder: use the other function for struct types"); Instruction* instr = make_instr(OpKind::Value_GetPointer, false, this->currentBlock, ptr->getType(), { ptr, ptrIndex }); @@ -1453,10 +1455,10 @@ namespace fir Value* IRBuilder::Select(Value* cond, Value* one, Value* two, const std::string& vname) { if(!cond->getType()->isBoolType()) - error("cond is not a boolean type (got '%s')", cond->getType()); + error("irbuilder: cond is not a boolean type (got '%s')", cond->getType()); if(one->getType() != two->getType()) - error("Non-identical types for operands (got '%s' and '%s')", one->getType(), two->getType()); + error("irbuilder: non-identical types for operands (got '%s' and '%s')", one->getType(), two->getType()); Instruction* instr = make_instr(OpKind::Value_Select, false, this->currentBlock, one->getType(), { cond, one, two }); return this->addInstruction(instr, vname); @@ -1466,7 +1468,7 @@ namespace fir Value* IRBuilder::Sizeof(Type* t, const std::string& vname) { - Instruction* instr = make_instr(OpKind::Misc_Sizeof, false, this->currentBlock, Type::getInt64(), + Instruction* instr = make_instr(OpKind::Misc_Sizeof, false, this->currentBlock, Type::getNativeWord(), { ConstantValue::getZeroValue(t) }); return this->addInstruction(instr, vname); @@ -1486,37 +1488,15 @@ namespace fir - Value* IRBuilder::PointerAdd(Value* ptr, Value* num, const std::string& vname) - { - if(!ptr->getType()->isPointerType()) - error("ptr is not a pointer type (got '%s')", ptr->getType()); - - if(!num->getType()->isIntegerType()) - error("num is not an integer type (got '%s')", num->getType()); - - Instruction* instr = make_instr(OpKind::Value_PointerAddition, false, this->currentBlock, ptr->getType(), { ptr, num }); - return this->addInstruction(instr, vname); - } - - Value* IRBuilder::PointerSub(Value* ptr, Value* num, const std::string& vname) - { - if(!ptr->getType()->isPointerType()) - error("ptr is not a pointer type (got '%s')", ptr->getType()); - - if(!num->getType()->isIntegerType()) - error("num is not an integer type (got '%s')", num->getType()); - - Instruction* instr = make_instr(OpKind::Value_PointerSubtraction, false, this->currentBlock, ptr->getType(), { ptr, num }); - return this->addInstruction(instr, vname); - } - - Value* IRBuilder::InsertValue(Value* val, const std::vector& inds, Value* elm, const std::string& vname) { Type* t = val->getType(); if(!t->isStructType() && !t->isClassType() && !t->isTupleType() && !t->isArrayType()) - error("val is not an aggregate type (have '%s')", t); + error("irbuilder: val is not an aggregate type (have '%s')", t); + + if(inds.size() != 1) + error("irbuilder: must have exactly one index!"); Type* et = 0; if(t->isStructType()) et = t->toStructType()->getElementN(inds[0]); @@ -1528,16 +1508,17 @@ namespace fir if(elm->getType() != et) { - error("Mismatched types for value and element -- trying to insert '%s' into '%s'", + error("irbuilder: mismatched types for value and element -- trying to insert '%s' into '%s'", elm->getType(), et); } + int ofs = 0; if(t->isClassType()) ofs = 1; //! to account for vtable std::vector args = { val, elm }; for(auto id : inds) - args.push_back(fir::ConstantInt::getInt64(id + ofs)); + args.push_back(fir::ConstantInt::getNative(id + ofs)); // note: no sideeffects, since we return a new aggregate Instruction* instr = make_instr(OpKind::Value_InsertValue, false, this->currentBlock, t, args); @@ -1548,7 +1529,10 @@ namespace fir { Type* t = val->getType(); if(!t->isStructType() && !t->isClassType() && !t->isTupleType() && !t->isArrayType()) - error("val is not an aggregate type (have '%s')", t); + error("irbuilder: val is not an aggregate type (have '%s')", t); + + if(inds.size() != 1) + error("irbuilder: must have exactly one index!"); Type* et = 0; if(t->isStructType()) et = t->toStructType()->getElementN(inds[0]); @@ -1563,7 +1547,7 @@ namespace fir std::vector args = { val }; for(auto id : inds) - args.push_back(fir::ConstantInt::getInt64(id + ofs)); + args.push_back(fir::ConstantInt::getNative(id + ofs)); Instruction* instr = make_instr(OpKind::Value_ExtractValue, false, this->currentBlock, et, args); @@ -1575,7 +1559,7 @@ namespace fir { Type* t = val->getType(); if(!t->isStructType() && !t->isClassType()) - error("val is not an aggregate type with named members (class or struct) (have '%s')", t); + error("irbuilder: val is not an aggregate type with named members (class or struct) (have '%s')", t); size_t ind = 0; if(t->isStructType()) ind = t->toStructType()->getElementIndex(n); @@ -1589,7 +1573,7 @@ namespace fir { Type* t = val->getType(); if(!t->isStructType() && !t->isClassType()) - error("val is not an aggregate type with named members (class or struct) (have '%s')", t); + error("irbuilder: val is not an aggregate type with named members (class or struct) (have '%s')", t); size_t ind = 0; @@ -1614,7 +1598,7 @@ namespace fir Value* IRBuilder::GetSAAData(Value* arr, const std::string& vname) { if(!isSAAType(arr->getType())) - error("thing is not an SAA type (got '%s')", arr->getType()); + error("irbuilder: thing is not an SAA type (got '%s')", arr->getType()); Instruction* instr = make_instr(OpKind::SAA_GetData, false, this->currentBlock, getSAAElmType(arr->getType())->getMutablePointerTo(), { arr }); @@ -1625,12 +1609,12 @@ namespace fir Value* IRBuilder::SetSAAData(Value* arr, Value* val, const std::string& vname) { if(!isSAAType(arr->getType())) - error("thing is not an SAA type (got '%s')", arr->getType()); + error("irbuilder: thing is not an SAA type (got '%s')", arr->getType()); auto t = getSAAElmType(arr->getType()); if(val->getType() != t->getMutablePointerTo()) { - error("val is not a pointer to elm type (need '%s', have '%s')", + error("irbuilder: val is not a pointer to elm type (need '%s', have '%s')", t->getMutablePointerTo(), val->getType()); } @@ -1645,10 +1629,10 @@ namespace fir Value* IRBuilder::GetSAALength(Value* arr, const std::string& vname) { if(!isSAAType(arr->getType())) - error("thing is not an SAA type (got '%s')", arr->getType()); + error("irbuilder: thing is not an SAA type (got '%s')", arr->getType()); Instruction* instr = make_instr(OpKind::SAA_GetLength, false, this->currentBlock, - fir::Type::getInt64(), { arr }); + fir::Type::getNativeWord(), { arr }); return this->addInstruction(instr, vname); } @@ -1656,10 +1640,10 @@ namespace fir Value* IRBuilder::SetSAALength(Value* arr, Value* val, const std::string& vname) { if(!isSAAType(arr->getType())) - error("thing is not an SAA type (got '%s')", arr->getType()); + error("irbuilder: thing is not an SAA type (got '%s')", arr->getType()); - if(val->getType() != fir::Type::getInt64()) - error("val is not an int64"); + if(val->getType() != fir::Type::getNativeWord()) + error("irbuilder: val is not an int64"); Instruction* instr = make_instr(OpKind::SAA_SetLength, true, this->currentBlock, arr->getType(), { arr, val }); @@ -1672,10 +1656,10 @@ namespace fir Value* IRBuilder::GetSAACapacity(Value* arr, const std::string& vname) { if(!isSAAType(arr->getType())) - error("thing is not an SAA type (got '%s')", arr->getType()); + error("irbuilder: thing is not an SAA type (got '%s')", arr->getType()); Instruction* instr = make_instr(OpKind::SAA_GetCapacity, false, this->currentBlock, - fir::Type::getInt64(), { arr }); + fir::Type::getNativeWord(), { arr }); return this->addInstruction(instr, vname); } @@ -1683,10 +1667,10 @@ namespace fir Value* IRBuilder::SetSAACapacity(Value* arr, Value* val, const std::string& vname) { if(!isSAAType(arr->getType())) - error("thing is not an SAA type (got '%s')", arr->getType()); + error("irbuilder: thing is not an SAA type (got '%s')", arr->getType()); - if(val->getType() != fir::Type::getInt64()) - error("val is not an int64"); + if(val->getType() != fir::Type::getNativeWord()) + error("irbuilder: val is not an int64"); Instruction* instr = make_instr(OpKind::SAA_SetCapacity, true, this->currentBlock, arr->getType(), { arr, val }); @@ -1699,10 +1683,10 @@ namespace fir Value* IRBuilder::GetSAARefCountPointer(Value* arr, const std::string& vname) { if(!isSAAType(arr->getType())) - error("thing is not an SAA type (got '%s')", arr->getType()); + error("irbuilder: thing is not an SAA type (got '%s')", arr->getType()); Instruction* instr = make_instr(OpKind::SAA_GetRefCountPtr, false, this->currentBlock, - fir::Type::getInt64Ptr(), { arr }); + fir::Type::getNativeWordPtr(), { arr }); return this->addInstruction(instr, vname); } @@ -1710,10 +1694,10 @@ namespace fir Value* IRBuilder::SetSAARefCountPointer(Value* arr, Value* val, const std::string& vname) { if(!isSAAType(arr->getType())) - error("thing is not an SAA type (got '%s')", arr->getType()); + error("irbuilder: thing is not an SAA type (got '%s')", arr->getType()); - if(val->getType() != fir::Type::getInt64()->getPointerTo()) - error("val is not an int64 pointer"); + if(val->getType() != fir::Type::getNativeWord()->getPointerTo()) + error("irbuilder: val is not an int64 pointer"); Instruction* instr = make_instr(OpKind::SAA_SetRefCountPtr, true, this->currentBlock, arr->getType(), { arr, val }); @@ -1730,10 +1714,10 @@ namespace fir void IRBuilder::SetSAARefCount(Value* arr, Value* val, const std::string& vname) { - if(val->getType() != fir::Type::getInt64()) - error("val is not an int64"); + if(val->getType() != fir::Type::getNativeWord()) + error("irbuilder: val is not an int64"); - this->WritePtr(val, this->PointerTypeCast(this->GetSAARefCountPointer(arr), fir::Type::getMutInt64Ptr())); + this->WritePtr(val, this->PointerTypeCast(this->GetSAARefCountPointer(arr), fir::Type::getNativeWordPtr()->getMutablePointerVersion())); } @@ -1760,7 +1744,7 @@ namespace fir Value* IRBuilder::GetArraySliceData(Value* slc, const std::string& vname) { if(!slc->getType()->isArraySliceType()) - error("slc is not an array slice type (got '%s')", slc->getType()); + error("irbuilder: slc is not an array slice type (got '%s')", slc->getType()); auto st = slc->getType()->toArraySliceType(); auto et = st->getElementType(); @@ -1774,7 +1758,7 @@ namespace fir Value* IRBuilder::SetArraySliceData(Value* slc, Value* val, const std::string& vname) { if(!slc->getType()->isArraySliceType()) - error("slc is not an array slice type (got '%s')", slc->getType()); + error("irbuilder: slc is not an array slice type (got '%s')", slc->getType()); auto st = slc->getType()->toArraySliceType(); auto et = st->getElementType(); @@ -1783,7 +1767,7 @@ namespace fir if(val->getType() != pt) { if(pt->getPointerElementType() != val->getType()->getPointerElementType() || (pt->isMutablePointer() && val->getType()->isImmutablePointer())) - error("val is not a pointer to elm type (need '%s', have '%s')", pt, val->getType()); + error("irbuilder: val is not a pointer to elm type (need '%s', have '%s')", pt, val->getType()); } Instruction* instr = make_instr(OpKind::ArraySlice_SetData, true, this->currentBlock, @@ -1796,10 +1780,10 @@ namespace fir Value* IRBuilder::GetArraySliceLength(Value* slc, const std::string& vname) { if(!slc->getType()->isArraySliceType()) - error("slc is not an array slice type (got '%s')", slc->getType()); + error("irbuilder: slc is not an array slice type (got '%s')", slc->getType()); Instruction* instr = make_instr(OpKind::ArraySlice_GetLength, false, this->currentBlock, - fir::Type::getInt64(), { slc }); + fir::Type::getNativeWord(), { slc }); return this->addInstruction(instr, vname); } @@ -1807,10 +1791,10 @@ namespace fir Value* IRBuilder::SetArraySliceLength(Value* slc, Value* val, const std::string& vname) { if(!slc->getType()->isArraySliceType()) - error("slc is not an array slice type (got '%s')", slc->getType()); + error("irbuilder: slc is not an array slice type (got '%s')", slc->getType()); - if(val->getType() != fir::Type::getInt64()) - error("val is not an int64"); + if(val->getType() != fir::Type::getNativeWord()) + error("irbuilder: val is not an int64"); Instruction* instr = make_instr(OpKind::ArraySlice_SetLength, true, this->currentBlock, slc->getType(), { slc, val }); @@ -1828,9 +1812,9 @@ namespace fir Value* IRBuilder::GetAnyTypeID(Value* any, const std::string& vname) { if(!any->getType()->isAnyType()) - error("not any type (got '%s')", any->getType()); + error("irbuilder: not any type (got '%s')", any->getType()); - Instruction* instr = make_instr(OpKind::Any_GetTypeID, false, this->currentBlock, fir::Type::getUint64(), { any }); + Instruction* instr = make_instr(OpKind::Any_GetTypeID, false, this->currentBlock, fir::Type::getNativeUWord(), { any }); return this->addInstruction(instr, vname); } @@ -1838,10 +1822,10 @@ namespace fir Value* IRBuilder::SetAnyTypeID(Value* any, Value* val, const std::string& vname) { if(!any->getType()->isAnyType()) - error("not any type (got '%s')", any->getType()); + error("irbuilder: not any type (got '%s')", any->getType()); - else if(val->getType() != fir::Type::getUint64()) - error("val is not a uint64"); + else if(val->getType() != fir::Type::getNativeUWord()) + error("irbuilder: val is not a uint64"); Instruction* instr = make_instr(OpKind::Any_SetTypeID, true, this->currentBlock, fir::Type::getAny(), { any, val }); @@ -1852,7 +1836,7 @@ namespace fir Value* IRBuilder::GetAnyData(Value* any, const std::string& vname) { if(!any->getType()->isAnyType()) - error("not any type (got '%s')", any->getType()); + error("irbuilder: not any type (got '%s')", any->getType()); Instruction* instr = make_instr(OpKind::Any_GetData, false, this->currentBlock, fir::ArrayType::get(fir::Type::getInt8(), BUILTIN_ANY_DATA_BYTECOUNT), { any }); @@ -1863,10 +1847,10 @@ namespace fir Value* IRBuilder::SetAnyData(Value* any, Value* val, const std::string& vname) { if(!any->getType()->isAnyType()) - error("not any type (got '%s')", any->getType()); + error("irbuilder: not any type (got '%s')", any->getType()); else if(val->getType() != fir::ArrayType::get(fir::Type::getInt8(), BUILTIN_ANY_DATA_BYTECOUNT)) - error("val is not array type (got '%s')", val->getType()); + error("irbuilder: val is not array type (got '%s')", val->getType()); Instruction* instr = make_instr(OpKind::Any_SetData, true, this->currentBlock, fir::Type::getAny(), { any, val }); @@ -1877,9 +1861,9 @@ namespace fir Value* IRBuilder::GetAnyRefCountPointer(Value* arr, const std::string& vname) { if(!arr->getType()->isAnyType()) - error("arr is not an any type (got '%s')", arr->getType()); + error("irbuilder: arr is not an any type (got '%s')", arr->getType()); - Instruction* instr = make_instr(OpKind::Any_GetRefCountPtr, false, this->currentBlock, fir::Type::getInt64Ptr(), { arr }); + Instruction* instr = make_instr(OpKind::Any_GetRefCountPtr, false, this->currentBlock, fir::Type::getNativeWordPtr(), { arr }); return this->addInstruction(instr, vname); } @@ -1887,10 +1871,10 @@ namespace fir Value* IRBuilder::SetAnyRefCountPointer(Value* arr, Value* val, const std::string& vname) { if(!arr->getType()->isAnyType()) - error("arr is not an any type (got '%s')", arr->getType()); + error("irbuilder: arr is not an any type (got '%s')", arr->getType()); - if(val->getType() != fir::Type::getInt64()->getPointerTo()) - error("val is not an int64 pointer"); + if(val->getType() != fir::Type::getNativeWord()->getPointerTo()) + error("irbuilder: val is not an int64 pointer"); Instruction* instr = make_instr(OpKind::Any_SetRefCountPtr, true, this->currentBlock, arr->getType(), { arr, val }); @@ -1906,10 +1890,10 @@ namespace fir void IRBuilder::SetAnyRefCount(Value* arr, Value* val, const std::string& vname) { - if(val->getType() != fir::Type::getInt64()) - error("val is not an int64"); + if(val->getType() != fir::Type::getNativeWord()) + error("irbuilder: val is not an int64"); - this->WritePtr(val, this->PointerTypeCast(this->GetAnyRefCountPointer(arr), fir::Type::getMutInt64Ptr())); + this->WritePtr(val, this->PointerTypeCast(this->GetAnyRefCountPointer(arr), fir::Type::getNativeWordPtr()->getMutablePointerVersion())); } @@ -1930,10 +1914,10 @@ namespace fir Value* IRBuilder::GetRangeLower(Value* range, const std::string& vname) { if(!range->getType()->isRangeType()) - error("range is not a range type (have '%s')", range->getType()); + error("irbuilder: range is not a range type (have '%s')", range->getType()); Instruction* instr = make_instr(OpKind::Range_GetLower, false, this->currentBlock, - fir::Type::getInt64(), { range }); + fir::Type::getNativeWord(), { range }); return this->addInstruction(instr, vname); } @@ -1941,10 +1925,10 @@ namespace fir Value* IRBuilder::SetRangeLower(Value* range, Value* val, const std::string& vname) { if(!range->getType()->isRangeType()) - error("range is not a range type (got '%s')", range->getType()); + error("irbuilder: range is not a range type (got '%s')", range->getType()); if(!val->getType()->isIntegerType()) - error("val is not an integer type (got '%s')", val->getType()); + error("irbuilder: val is not an integer type (got '%s')", val->getType()); Instruction* instr = make_instr(OpKind::Range_SetLower, true, this->currentBlock, fir::Type::getRange(), { range, val }); @@ -1955,10 +1939,10 @@ namespace fir Value* IRBuilder::GetRangeUpper(Value* range, const std::string& vname) { if(!range->getType()->isRangeType()) - error("range is not a range type (have '%s')", range->getType()); + error("irbuilder: range is not a range type (have '%s')", range->getType()); Instruction* instr = make_instr(OpKind::Range_GetUpper, false, this->currentBlock, - fir::Type::getInt64(), { range }); + fir::Type::getNativeWord(), { range }); return this->addInstruction(instr, vname); } @@ -1966,10 +1950,10 @@ namespace fir Value* IRBuilder::SetRangeUpper(Value* range, Value* val, const std::string& vname) { if(!range->getType()->isRangeType()) - error("range is not a range type (got '%s')", range->getType()); + error("irbuilder: range is not a range type (got '%s')", range->getType()); if(!val->getType()->isIntegerType()) - error("val is not an integer type (got '%s')", val->getType()); + error("irbuilder: val is not an integer type (got '%s')", val->getType()); Instruction* instr = make_instr(OpKind::Range_SetUpper, true, this->currentBlock, fir::Type::getRange(), { range, val }); @@ -1980,10 +1964,10 @@ namespace fir Value* IRBuilder::GetRangeStep(Value* range, const std::string& vname) { if(!range->getType()->isRangeType()) - error("range is not a range type (have '%s')", range->getType()); + error("irbuilder: range is not a range type (have '%s')", range->getType()); Instruction* instr = make_instr(OpKind::Range_GetStep, false, this->currentBlock, - fir::Type::getInt64(), { range }); + fir::Type::getNativeWord(), { range }); return this->addInstruction(instr, vname); } @@ -1991,10 +1975,10 @@ namespace fir Value* IRBuilder::SetRangeStep(Value* range, Value* val, const std::string& vname) { if(!range->getType()->isRangeType()) - error("range is not a range type (got '%s')", range->getType()); + error("irbuilder: range is not a range type (got '%s')", range->getType()); if(!val->getType()->isIntegerType()) - error("val is not an integer type (got '%s')", val->getType()); + error("irbuilder: val is not an integer type (got '%s')", val->getType()); Instruction* instr = make_instr(OpKind::Range_SetStep, true, this->currentBlock, fir::Type::getRange(), { range, val }); @@ -2008,10 +1992,10 @@ namespace fir Value* IRBuilder::GetEnumCaseIndex(Value* ecs, const std::string& vname) { if(!ecs->getType()->isEnumType()) - error("enum is not an enum type (got '%s')", ecs->getType()); + error("irbuilder: enum is not an enum type (got '%s')", ecs->getType()); Instruction* instr = make_instr(OpKind::Enum_GetIndex, true, this->currentBlock, - fir::Type::getInt64(), { ecs }); + fir::Type::getNativeWord(), { ecs }); return this->addInstruction(instr, vname); } @@ -2019,10 +2003,10 @@ namespace fir Value* IRBuilder::SetEnumCaseIndex(Value* ecs, Value* idx, const std::string& vname) { if(!ecs->getType()->isEnumType()) - error("enum is not an enum type (got '%s')", ecs->getType()); + error("irbuilder: enum is not an enum type (got '%s')", ecs->getType()); if(!idx->getType()->isIntegerType()) - error("index is not an integer type (got '%s')", idx->getType()); + error("irbuilder: index is not an integer type (got '%s')", idx->getType()); Instruction* instr = make_instr(OpKind::Enum_SetIndex, true, this->currentBlock, ecs->getType(), { ecs, idx }); @@ -2033,7 +2017,7 @@ namespace fir Value* IRBuilder::GetEnumCaseValue(Value* ecs, const std::string& vname) { if(!ecs->getType()->isEnumType()) - error("enum is not an enum type (got '%s')", ecs->getType()); + error("irbuilder: enum is not an enum type (got '%s')", ecs->getType()); Instruction* instr = make_instr(OpKind::Enum_GetValue, true, this->currentBlock, ecs->getType()->toEnumType()->getCaseType(), { ecs }); @@ -2044,11 +2028,11 @@ namespace fir Value* IRBuilder::SetEnumCaseValue(Value* ecs, Value* val, const std::string& vname) { if(!ecs->getType()->isEnumType()) - error("enum is not an enum type (got '%s')", ecs->getType()); + error("irbuilder: enum is not an enum type (got '%s')", ecs->getType()); if(ecs->getType()->toEnumType()->getCaseType() != val->getType()) { - error("value type mismatch (enum case type is '%s', value type is '%s'", + error("irbuilder: value type mismatch (enum case type is '%s', value type is '%s'", ecs->getType()->toEnumType()->getCaseType(), val->getType()); } @@ -2067,7 +2051,7 @@ namespace fir Value* IRBuilder::ReadPtr(Value* ptr, const std::string& vname) { if(!ptr->getType()->isPointerType()) - error("ptr is not pointer type (got '%s')", ptr->getType()); + error("irbuilder: ptr is not pointer type (got '%s')", ptr->getType()); Instruction* instr = make_instr(OpKind::Value_ReadPtr, false, this->currentBlock, ptr->getType()->getPointerElementType(), { ptr }); return this->addInstruction(instr, vname); @@ -2076,10 +2060,10 @@ namespace fir void IRBuilder::WritePtr(Value* v, Value* ptr) { if(!ptr->getType()->isPointerType()) - error("ptr is not pointer type (got '%s')", ptr->getType()); + error("irbuilder: ptr is not pointer type (got '%s')", ptr->getType()); if(ptr->getType()->isImmutablePointer()) - error("Cannot store value to immutable pointer type '%s'", ptr->getType()); + error("irbuilder: cannot store value to immutable pointer type '%s'", ptr->getType()); auto vt = v->getType(); auto pt = ptr->getType(); @@ -2092,7 +2076,7 @@ namespace fir //* so we can safely pass this onto the translation layer without worrying about it. // if((vt->isPointerType() && pt->isPointerType() && vt->getPointerElementType() == pt->getPointerElementType()) == false) - error("ptr is not a pointer to type of value (base types '%s' -> '%s' differ)", vt, pt->getPointerElementType()); + error("irbuilder: ptr is not a pointer to type of value (base types '%s' -> '%s' differ)", vt, pt->getPointerElementType()); } @@ -2103,7 +2087,7 @@ namespace fir Value* IRBuilder::CreateLValue(Type* type, const std::string& vname) { - // ok... + // needs to be hoisted also Instruction* instr = make_instr(OpKind::Value_CreateLVal, true, this->currentBlock, type, { ConstantValue::getZeroValue(type) }, Value::Kind::lvalue); @@ -2114,6 +2098,8 @@ namespace fir auto parent = this->currentBlock->getParentFunction(); iceAssert(parent); + parent->addStackAllocation(type); + // get the entry block auto entry = parent->getBlockList().front(); iceAssert(entry); @@ -2126,7 +2112,7 @@ namespace fir Value* IRBuilder::CreateConstLValue(Value* val, const std::string& vname) { - // ok... + // needs to be hoisted also Instruction* instr = make_instr(OpKind::Value_CreateLVal, true, this->currentBlock, val->getType(), { ConstantValue::getZeroValue(val->getType()) }, Value::Kind::lvalue); @@ -2140,6 +2126,8 @@ namespace fir auto parent = this->currentBlock->getParentFunction(); iceAssert(parent); + parent->addStackAllocation(val->getType()); + // get the entry block auto entry = parent->getBlockList().front(); iceAssert(entry); @@ -2152,13 +2140,13 @@ namespace fir void IRBuilder::Store(Value* val, Value* lval) { if(lval->isclvalue()) - error("cannot store to constant lvalue"); + error("irbuilder: cannot store to constant lvalue"); else if(!lval->islvalue()) - error("cannot store to non-lvalue"); + error("irbuilder: cannot store to non-lvalue"); else if(val->getType() != lval->getType()) - error("cannot store value of type '%s' to lvalue of type '%s'", val->getType(), lval->getType()); + error("irbuilder: cannot store value of type '%s' to lvalue of type '%s'", val->getType(), lval->getType()); // ok... Instruction* instr = make_instr(OpKind::Value_Store, true, this->currentBlock, Type::getVoid(), { val, lval }); @@ -2168,7 +2156,7 @@ namespace fir Value* IRBuilder::Dereference(Value* val, const std::string& vname) { if(!val->getType()->isPointerType()) - error("cannot dereference non-pointer type '%s'", val->getType()); + error("irbuilder: cannot dereference non-pointer type '%s'", val->getType()); Instruction* instr = make_instr(OpKind::Value_Dereference, true, this->currentBlock, val->getType()->getPointerElementType(), { val }, val->getType()->isMutablePointer() ? Value::Kind::lvalue : Value::Kind::clvalue); @@ -2179,7 +2167,7 @@ namespace fir Value* IRBuilder::AddressOf(Value* lval, bool mut, const std::string& vname) { if(!lval->islorclvalue()) - error("cannot take the address of a non-lvalue"); + error("irbuilder: cannot take the address of a non-lvalue"); // ok... Instruction* instr = make_instr(OpKind::Value_AddressOf, true, this->currentBlock, @@ -2192,47 +2180,45 @@ namespace fir Value* IRBuilder::SetUnionVariantData(Value* unn, size_t id, Value* data, const std::string& vname) { if(!unn->getType()->isUnionType()) - error("'%s' is not a union type", unn->getType()); + error("irbuilder: '%s' is not a union type", unn->getType()); auto ut = unn->getType()->toUnionType(); if(data->getType() != ut->getVariant(id)->getInteriorType()) - error("cannot store data '%s' into union variant '%s'", data->getType(), ut->getVariant(id)->getInteriorType()); + error("irbuilder: cannot store data '%s' into union variant '%s'", data->getType(), ut->getVariant(id)->getInteriorType()); Instruction* instr = make_instr(OpKind::Union_SetValue, true, this->currentBlock, unn->getType(), - { unn, fir::ConstantInt::getInt64(id), data }); + { unn, fir::ConstantInt::getNative(id), data }); return this->addInstruction(instr, vname); } Value* IRBuilder::GetUnionVariantData(Value* unn, size_t id, const std::string& vname) { if(!unn->getType()->isUnionType()) - error("'%s' is not a union type", unn->getType()); + error("irbuilder: '%s' is not a union type", unn->getType()); auto ut = unn->getType()->toUnionType(); Instruction* instr = make_instr(OpKind::Union_GetValue, true, this->currentBlock, ut->getVariant(id)->getInteriorType(), - { unn, fir::ConstantInt::getInt64(id) }); + { unn, fir::ConstantInt::getNative(id) }); return this->addInstruction(instr, vname); } Value* IRBuilder::GetUnionVariantID(Value* unn, const std::string& vname) { if(!unn->getType()->isUnionType()) - error("'%s' is not a union type", unn->getType()); + error("irbuilder: '%s' is not a union type", unn->getType()); - Instruction* instr = make_instr(OpKind::Union_GetVariantID, true, this->currentBlock, fir::Type::getInt64(), { unn }); + Instruction* instr = make_instr(OpKind::Union_GetVariantID, true, this->currentBlock, fir::Type::getNativeWord(), { unn }); return this->addInstruction(instr, vname); } - - Value* IRBuilder::SetUnionVariantID(Value* unn, size_t id, const std::string& vname) { if(!unn->getType()->isUnionType()) - error("'%s' is not a union type", unn->getType()); + error("irbuilder: '%s' is not a union type", unn->getType()); Instruction* instr = make_instr(OpKind::Union_SetVariantID, true, this->currentBlock, unn->getType(), - { unn, fir::ConstantInt::getInt64(id) }); + { unn, fir::ConstantInt::getNative(id) }); return this->addInstruction(instr, vname); } diff --git a/source/fir/Instruction.cpp b/source/fir/Instruction.cpp index f6ff2179..465ac7e6 100644 --- a/source/fir/Instruction.cpp +++ b/source/fir/Instruction.cpp @@ -1,5 +1,5 @@ // Instruction.cpp -// Copyright (c) 2014 - 2016, zhiayang@gmail.com +// Copyright (c) 2014 - 2016, zhiayang // Licensed under the Apache License Version 2.0. #include "ir/block.h" @@ -11,7 +11,7 @@ namespace fir { - static util::MemoryPool value_pool(2048); + static util::MemoryPool value_pool(65536); Instruction::Instruction(OpKind kind, bool sideeff, IRBlock* parent, Type* out, const std::vector& vals) @@ -29,7 +29,7 @@ namespace fir Value* Instruction::getResult() { if(this->realOutput) return this->realOutput; - error("Calling getActualValue() when not in function! (no real value)"); + error("calling getActualValue() when not in function! (no real value)"); } bool Instruction::hasSideEffects() @@ -127,8 +127,6 @@ namespace fir case OpKind::Misc_Sizeof: instrname = "sizeof"; break; case OpKind::Branch_UnCond: instrname = "jump"; break; case OpKind::Branch_Cond: instrname = "branch"; break; - case OpKind::Value_PointerAddition: instrname = "ptradd"; break; - case OpKind::Value_PointerSubtraction: instrname = "ptrsub"; break; case OpKind::Value_CreatePHI: instrname = "phi"; break; @@ -176,7 +174,7 @@ namespace fir case OpKind::Value_AddressOf: instrname = "addrof"; break; case OpKind::Value_Store: instrname = "store"; break; - case OpKind::Value_Dereference: instrname = "dereferece"; break; + case OpKind::Value_Dereference: instrname = "dereference"; break; case OpKind::Value_CreateLVal: instrname = "make_lval"; break; case OpKind::Unreachable: instrname = ""; break; @@ -235,7 +233,7 @@ namespace fir } else if(IRBlock* ib = dcast(IRBlock, op)) { - ops += "$" + ib->getName().str(); + ops += strprintf("$%zu/%s", ib->id, ib->getName().str()); } else { diff --git a/source/fir/Module.cpp b/source/fir/Module.cpp index 15211170..31937751 100644 --- a/source/fir/Module.cpp +++ b/source/fir/Module.cpp @@ -1,9 +1,11 @@ // Module.cpp -// Copyright (c) 2014 - 2016, zhiayang@gmail.com +// Copyright (c) 2014 - 2016, zhiayang // Licensed under the Apache License Version 2.0. #include "gluecode.h" + #include "ir/module.h" +#include "ir/irbuilder.h" #include @@ -14,17 +16,66 @@ namespace fir this->moduleName = nm; } - GlobalVariable* Module::createGlobalVariable(const Identifier& ident, Type* type, ConstantValue* initVal, bool isImmut, LinkageType linkage) + + void Module::finaliseGlobalConstructors() { - // if(this->globals.find(ident) != this->globals.end()) - // error("Already have a global with name '%s'", ident.str()); + auto builder = IRBuilder(this); + auto entryfunc = this->entryFunction; + + if(!entryfunc) + { + // keep trying various things. + std::vector trymains = { "main", "_FF" + this->getModuleName() + "4main_FAv" }; + for(const auto& m : trymains) + { + entryfunc = this->getFunction(Identifier(m, IdKind::Name)); + if(entryfunc) break; + } + + if(entryfunc) + this->entryFunction = entryfunc; + + else + error("fir: no entry point marked with '@entry', and no 'main' function; cannot compile program"); + } + + // it doesn't actually matter what the entry function is named -- we just need to insert some instructions at the beginning. + iceAssert(entryfunc); + { + iceAssert(entryfunc->getBlockList().size() > 0); + + auto oldentry = entryfunc->getBlockList()[0]; + auto newentry = new IRBlock(entryfunc); + newentry->setName("entryblock_entry"); + + auto& blklist = entryfunc->getBlockList(); + blklist.insert(blklist.begin(), newentry); + + builder.setCurrentBlock(newentry); - // // this adds itself to the module list. - // return new GlobalVariable(ident, this, type, isImmut, linkage, initVal); + auto gif = this->getFunction(util::obfuscateIdentifier(BUILTIN_GLOBAL_INIT_FUNCTION_NAME)); + if(!gif) error("fir: failed to find global init function"); + builder.Call(gif); + builder.UnCondBranch(oldentry); + } + } + + + + + + + + + + + + GlobalVariable* Module::createGlobalVariable(const Identifier& ident, Type* type, ConstantValue* initVal, bool isImmut, LinkageType linkage) + { GlobalVariable* gv = new GlobalVariable(ident, this, type, isImmut, linkage, initVal); if(this->globals.find(ident) != this->globals.end()) - error("Already have a global with name '%s'", ident.str()); + error("fir: already have a global with name '%s'", ident.str()); this->globals[ident] = gv; return gv; @@ -51,7 +102,7 @@ namespace fir GlobalVariable* Module::getGlobalVariable(const Identifier& id) { if(this->globals.find(id) == this->globals.end()) - error("No such global with name '%s'", id.str()); + error("fir: no such global with name '%s'", id.str()); return this->globals[id]; } @@ -75,7 +126,7 @@ namespace fir // TODO: should we make the vtable immutable? auto table = ConstantArray::get(ArrayType::get(FunctionType::get({ }, Type::getVoid()), cls->virtualMethodCount), methods); - auto vtab = this->createGlobalVariable(Identifier("__vtable_" + cls->getTypeName().mangled(), IdKind::Name), + auto vtab = this->createGlobalVariable(util::obfuscateIdentifier("vtable", cls->getTypeName().mangled()), table->getType(), table, true, LinkageType::External); this->vtables[cls] = { fmethods, vtab }; @@ -115,7 +166,7 @@ namespace fir Type* Module::getNamedType(const Identifier& id) { if(this->namedTypes.find(id) == this->namedTypes.end()) - error("No such type with name '%s'", id.str()); + error("fir: no such type with name '%s'", id.str()); return this->namedTypes[id]; } @@ -123,7 +174,7 @@ namespace fir void Module::addNamedType(const Identifier& id, Type* type) { if(this->namedTypes.find(id) != this->namedTypes.end()) - error("Type '%s' exists already", id.str()); + error("fir: type '%s' exists already", id.str()); this->namedTypes[id] = type; } @@ -141,7 +192,7 @@ namespace fir void Module::addFunction(Function* func) { if(this->functions.find(func->getName()) != this->functions.end()) - error("Function '%s' exists already", func->getName().str()); + error("fir: function '%s' exists already", func->getName().str()); this->functions[func->getName()] = func; } @@ -149,7 +200,7 @@ namespace fir void Module::removeFunction(Function* func) { if(this->functions.find(func->getName()) == this->functions.end()) - error("Function '%s' does not exist, cannot remove", func->getName().str()); + error("fir: function '%s' does not exist, cannot remove", func->getName().str()); this->functions.erase(func->getName()); } @@ -189,7 +240,7 @@ namespace fir { if(!this->functions[id]->getType()->isTypeEqual(ftype)) { - error("Function '%s' redeclared with different type (have '%s', new '%s')", id.str(), + error("fir: function '%s' redeclared with different type (have '%s', new '%s')", id.str(), this->functions[id]->getType(), ftype); } @@ -353,21 +404,21 @@ namespace fir { name = Identifier("memcpy", IdKind::Name); ft = FunctionType::get({ fir::Type::getMutInt8Ptr(), fir::Type::getInt8Ptr(), - fir::Type::getInt64(), fir::Type::getBool() }, + fir::Type::getNativeWord(), fir::Type::getBool() }, fir::Type::getVoid()); } else if(id == "memmove") { name = Identifier("memmove", IdKind::Name); ft = FunctionType::get({ fir::Type::getMutInt8Ptr(), fir::Type::getMutInt8Ptr(), - fir::Type::getInt64(), fir::Type::getBool() }, + fir::Type::getNativeWord(), fir::Type::getBool() }, fir::Type::getVoid()); } else if(id == "memset") { name = Identifier("memset", IdKind::Name); ft = FunctionType::get({ fir::Type::getMutInt8Ptr(), fir::Type::getInt8(), - fir::Type::getInt64(), fir::Type::getBool() }, + fir::Type::getNativeWord(), fir::Type::getBool() }, fir::Type::getVoid()); } else if(id == "memcmp") @@ -377,7 +428,7 @@ namespace fir name = Identifier("memcmp", IdKind::Name); ft = FunctionType::get({ fir::Type::getInt8Ptr(), fir::Type::getInt8Ptr(), - fir::Type::getInt64(), fir::Type::getBool() }, + fir::Type::getNativeWord(), fir::Type::getBool() }, fir::Type::getInt32()); } else if(id == "roundup_pow2") @@ -388,7 +439,7 @@ namespace fir // 40 -> 64 name = Identifier("roundup_pow2", IdKind::Name); - ft = FunctionType::get({ fir::Type::getInt64() }, fir::Type::getInt64()); + ft = FunctionType::get({ fir::Type::getNativeWord() }, fir::Type::getNativeWord()); } if(this->intrinsicFunctions.find(name) != this->intrinsicFunctions.end()) diff --git a/source/fir/Types/ArraySliceType.cpp b/source/fir/Types/ArraySliceType.cpp index d2dd19da..88dbd6ff 100644 --- a/source/fir/Types/ArraySliceType.cpp +++ b/source/fir/Types/ArraySliceType.cpp @@ -1,5 +1,5 @@ // ArraySliceType.cpp -// Copyright (c) 2014 - 2016, zhiayang@gmail.com +// Copyright (c) 2014 - 2016, zhiayang // Licensed under the Apache License Version 2.0. #include "ir/type.h" diff --git a/source/fir/Types/ArrayType.cpp b/source/fir/Types/ArrayType.cpp index e0f57459..997a6004 100644 --- a/source/fir/Types/ArrayType.cpp +++ b/source/fir/Types/ArrayType.cpp @@ -1,5 +1,5 @@ // Type.cpp -// Copyright (c) 2014 - 2016, zhiayang@gmail.com +// Copyright (c) 2014 - 2016, zhiayang // Licensed under the Apache License Version 2.0. #include "ir/type.h" diff --git a/source/fir/Types/ClassType.cpp b/source/fir/Types/ClassType.cpp index 4becc201..2e63e63f 100644 --- a/source/fir/Types/ClassType.cpp +++ b/source/fir/Types/ClassType.cpp @@ -1,5 +1,5 @@ // ClassType.cpp -// Copyright (c) 2014 - 2016, zhiayang@gmail.com +// Copyright (c) 2014 - 2016, zhiayang // Licensed under the Apache License Version 2.0. #include "errors.h" @@ -147,18 +147,46 @@ namespace fir - std::vector ClassType::getElements() + const std::vector& ClassType::getElements() { return this->typeList; } + std::vector ClassType::getAllElementsIncludingBase() + { + std::vector ret; + + std::function*)> + addMembers = [&addMembers](ClassType* cls, std::vector* mems) -> void { + + if(!cls) return; + + addMembers(cls->getBaseClass(), mems); + + for(auto f : cls->getElements()) + mems->push_back(f); + }; + + addMembers(this, &ret); + + return ret; + } + + + + + + + + + - std::vector ClassType::getInitialiserFunctions() + const std::vector& ClassType::getInitialiserFunctions() { return this->initialiserList; } - std::vector ClassType::getMethods() + const std::vector& ClassType::getMethods() { return this->methodList; } @@ -182,7 +210,7 @@ namespace fir return f; } - error("no such function with type '%s'", ftype); + error("no method with type '%s'", ftype); } @@ -304,7 +332,7 @@ namespace fir } else { - error("no such method named '%s' matching signature '%s' in virtual method table of class '%s'", + error("no method named '%s' matching signature '%s' in virtual method table of class '%s'", name, (Type*) ft, this->getTypeName().name); } } diff --git a/source/fir/Types/DynamicArrayType.cpp b/source/fir/Types/DynamicArrayType.cpp index 2c807f48..823ddbe4 100644 --- a/source/fir/Types/DynamicArrayType.cpp +++ b/source/fir/Types/DynamicArrayType.cpp @@ -1,5 +1,5 @@ // DynamicArrayType.cpp -// Copyright (c) 2014 - 2016, zhiayang@gmail.com +// Copyright (c) 2014 - 2016, zhiayang // Licensed under the Apache License Version 2.0. #include "ir/type.h" diff --git a/source/fir/Types/EnumType.cpp b/source/fir/Types/EnumType.cpp index eea39f11..3d85c620 100644 --- a/source/fir/Types/EnumType.cpp +++ b/source/fir/Types/EnumType.cpp @@ -1,5 +1,5 @@ // EnumType.cpp -// Copyright (c) 2014 - 2016, zhiayang@gmail.com +// Copyright (c) 2014 - 2016, zhiayang // Licensed under the Apache License Version 2.0. diff --git a/source/fir/Types/FunctionType.cpp b/source/fir/Types/FunctionType.cpp index 4bc92947..bf5717a0 100644 --- a/source/fir/Types/FunctionType.cpp +++ b/source/fir/Types/FunctionType.cpp @@ -1,5 +1,5 @@ // FunctionType.cpp -// Copyright (c) 2014 - 2016, zhiayang@gmail.com +// Copyright (c) 2014 - 2016, zhiayang // Licensed under the Apache License Version 2.0. #include "ir/type.h" @@ -75,7 +75,7 @@ namespace fir // function stuff - std::vector FunctionType::getArgumentTypes() + const std::vector& FunctionType::getArgumentTypes() { return this->functionParams; } diff --git a/source/fir/Types/OpaqueType.cpp b/source/fir/Types/OpaqueType.cpp new file mode 100644 index 00000000..546cf2b0 --- /dev/null +++ b/source/fir/Types/OpaqueType.cpp @@ -0,0 +1,52 @@ +// OpaqueType.cpp +// Copyright (c) 2019, zhiayang +// Licensed under the Apache License Version 2.0. + +#include "ir/type.h" +#include "ir/value.h" +#include "ir/constant.h" + + +namespace fir +{ + OpaqueType::OpaqueType(const std::string& name, size_t size) : Type(TypeKind::Opaque) + { + this->typeName = name; + this->typeSizeInBits = size; + } + + std::string OpaqueType::str() + { + return strprintf("opaque(%s)", this->typeName); + } + + std::string OpaqueType::encodedStr() + { + return strprintf("opaque(%s)", this->typeName); + } + + bool OpaqueType::isTypeEqual(Type* other) + { + return other && other->isOpaqueType() && other->toOpaqueType()->typeName == this->typeName; + } + + fir::Type* OpaqueType::substitutePlaceholders(const util::hash_map& subst) + { + return this; + } + + + static util::hash_map typeCache; + OpaqueType* OpaqueType::get(const std::string& name, size_t size) + { + if(size < 8) + error("types must be >= 8 bits (for now) (%s)", name); + + + if(auto it = typeCache.find(name); it != typeCache.end()) + return it->second; + + else + return (typeCache[name] = new OpaqueType(name, size)); + } +} diff --git a/source/fir/Types/PointerType.cpp b/source/fir/Types/PointerType.cpp index c2e8646e..1654aa34 100644 --- a/source/fir/Types/PointerType.cpp +++ b/source/fir/Types/PointerType.cpp @@ -1,5 +1,5 @@ // Type.cpp -// Copyright (c) 2014 - 2016, zhiayang@gmail.com +// Copyright (c) 2014 - 2016, zhiayang // Licensed under the Apache License Version 2.0. #include "ir/type.h" diff --git a/source/fir/Types/PrimitiveType.cpp b/source/fir/Types/PrimitiveType.cpp index 9c33871c..c049ce1e 100644 --- a/source/fir/Types/PrimitiveType.cpp +++ b/source/fir/Types/PrimitiveType.cpp @@ -1,5 +1,5 @@ // Type.cpp -// Copyright (c) 2014 - 2016, zhiayang@gmail.com +// Copyright (c) 2014 - 2016, zhiayang // Licensed under the Apache License Version 2.0. #include "ir/type.h" diff --git a/source/fir/Types/RawUnionType.cpp b/source/fir/Types/RawUnionType.cpp index 59ea993f..1305a9be 100644 --- a/source/fir/Types/RawUnionType.cpp +++ b/source/fir/Types/RawUnionType.cpp @@ -1,5 +1,5 @@ // RawUnionType.cpp -// Copyright (c) 2019, zhiayang@gmail.com +// Copyright (c) 2019, zhiayang // Licensed under the Apache License Version 2.0. @@ -22,7 +22,7 @@ namespace fir RawUnionType* RawUnionType::create(const Identifier& name, const util::hash_map& mems) { if(auto it = typeCache.find(name); it != typeCache.end()) - error("Union with name '%s' already exists", name.str()); + error("union with name '%s' already exists", name.str()); else return (typeCache[name] = new RawUnionType(name, mems)); @@ -71,7 +71,7 @@ namespace fir return this->variants.size(); } - util::hash_map RawUnionType::getVariants() + const util::hash_map& RawUnionType::getVariants() { return this->variants; } @@ -104,4 +104,4 @@ namespace fir return this; } -} \ No newline at end of file +} diff --git a/source/fir/Types/SingleTypes.cpp b/source/fir/Types/SingleTypes.cpp index dea9588c..22b001de 100644 --- a/source/fir/Types/SingleTypes.cpp +++ b/source/fir/Types/SingleTypes.cpp @@ -1,5 +1,5 @@ // SingleTypes.cpp -// Copyright (c) 2017, zhiayang@gmail.com +// Copyright (c) 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "errors.h" @@ -62,6 +62,9 @@ namespace fir + + + std::string ConstantNumberType::encodedStr() { return "number"; } bool ConstantNumberType::isSigned() { return this->_signed; } bool ConstantNumberType::isFloating() { return this->_floating; } @@ -116,9 +119,9 @@ namespace fir } else { - if(cnt->getMinBits() <= 63) + if(cnt->getMinBits() < fir::Type::getNativeWord()->getBitWidth()) { - return fir::Type::getInt64(); + return fir::Type::getNativeWord(); } else if(cnt->isSigned()) { @@ -126,10 +129,10 @@ namespace fir } else { - if(cnt->getMinBits() > 64) + if(cnt->getMinBits() > fir::Type::getNativeUWord()->getBitWidth()) error("constant number type '%s' requires too many bits", (Type*) cnt); - return fir::Type::getUint64(); + return fir::Type::getNativeUWord(); } } } @@ -190,8 +193,6 @@ namespace fir { return _substitute(subst, this); } - - } diff --git a/source/fir/Types/StructType.cpp b/source/fir/Types/StructType.cpp index 189444f7..307882f4 100644 --- a/source/fir/Types/StructType.cpp +++ b/source/fir/Types/StructType.cpp @@ -1,5 +1,5 @@ // StructType.cpp -// Copyright (c) 2014 - 2016, zhiayang@gmail.com +// Copyright (c) 2014 - 2016, zhiayang // Licensed under the Apache License Version 2.0. #include "errors.h" @@ -98,7 +98,7 @@ namespace fir return this->indexMap.find(name) != this->indexMap.end(); } - std::vector StructType::getElements() + const std::vector& StructType::getElements() { return this->typeList; } diff --git a/source/fir/Types/TupleType.cpp b/source/fir/Types/TupleType.cpp index b0b45e47..5b2180bc 100644 --- a/source/fir/Types/TupleType.cpp +++ b/source/fir/Types/TupleType.cpp @@ -1,5 +1,5 @@ // TupleType.cpp -// Copyright (c) 2014 - 2016, zhiayang@gmail.com +// Copyright (c) 2014 - 2016, zhiayang // Licensed under the Apache License Version 2.0. #include "errors.h" @@ -23,9 +23,9 @@ namespace fir return this->members[n]; } - std::vector TupleType::getElements() + const std::vector& TupleType::getElements() { - return std::vector(this->members.begin(), this->members.end()); + return this->members; } diff --git a/source/fir/Types/Type.cpp b/source/fir/Types/Type.cpp index f8c1cdf4..e3801e94 100644 --- a/source/fir/Types/Type.cpp +++ b/source/fir/Types/Type.cpp @@ -1,5 +1,5 @@ // Type.cpp -// Copyright (c) 2014 - 2016, zhiayang@gmail.com +// Copyright (c) 2014 - 2016, zhiayang // Licensed under the Apache License Version 2.0. #include "errors.h" @@ -20,6 +20,24 @@ namespace fir return tc; } + + static size_t nativeWordSize = 64; + void setNativeWordSizeInBits(size_t sz) + { + if(sz < 8 || sz > 64) + error("native word size must be >= 8 and < 64, %zu invalid", sz); + + // we're not gonna check any further, anything becomes your problem once you change this. + nativeWordSize = sz; + } + + size_t getNativeWordSizeInBits() + { + return nativeWordSize; + } + + + int getCastDistance(Type* from, Type* to) { if(from == to) return 0; @@ -158,6 +176,21 @@ namespace fir + PrimitiveType* Type::getNativeWord() + { + return PrimitiveType::getIntN(nativeWordSize); + } + + PrimitiveType* Type::getNativeUWord() + { + return PrimitiveType::getUintN(nativeWordSize); + } + + PointerType* Type::getNativeWordPtr() + { + return Type::getNativeWord()->getPointerTo()->toPointerType(); + } + std::string Type::typeListToString(const std::initializer_list& types, bool includeBraces) { return typeListToString(std::vector(types.begin(), types.end()), includeBraces); @@ -304,8 +337,8 @@ namespace fir else if(copy == VOID_TYPE_STRING) real = Type::getVoid(); // unspecified things - else if(copy == INTUNSPEC_TYPE_STRING) real = Type::getInt64(); - else if(copy == UINTUNSPEC_TYPE_STRING) real = Type::getUint64(); + else if(copy == INTUNSPEC_TYPE_STRING) real = Type::getNativeWord(); + else if(copy == UINTUNSPEC_TYPE_STRING) real = Type::getNativeUWord(); else if(copy == FLOAT_TYPE_STRING) real = Type::getFloat32(); else if(copy == DOUBLE_TYPE_STRING) real = Type::getFloat64(); @@ -555,6 +588,11 @@ namespace fir return static_cast(this); } + OpaqueType* Type::toOpaqueType() + { + if(this->kind != TypeKind::Opaque) error("not opaque type"); + return static_cast(this); + } @@ -718,6 +756,11 @@ namespace fir return this->kind == TypeKind::UnionVariant; } + bool Type::isOpaqueType() + { + return this->kind == TypeKind::Opaque; + } + // static getting functions @@ -974,26 +1017,29 @@ namespace fir size_t getSizeOfType(Type* type) { auto ptrt = fir::Type::getInt8Ptr(); - auto i64t = fir::Type::getInt64(); + auto wordty = fir::Type::getNativeWord(); if(type->isVoidType()) return 0; else if(type->isBoolType()) return 1; - else if(type->isPointerType() || type->isFunctionType()) return sizeof(void*); else if(type->isPrimitiveType()) return type->getBitWidth() / 8; - else if(type->isArraySliceType()) return getAggregateSize({ ptrt, i64t }); - else if(type->isStringType() || type->isDynamicArrayType()) return getAggregateSize({ ptrt, i64t, i64t, ptrt }); - else if(type->isRangeType()) return getAggregateSize({ i64t, i64t, i64t }); + else if(type->isArraySliceType()) return getAggregateSize({ ptrt, wordty }); + else if(type->isStringType() || type->isDynamicArrayType()) return getAggregateSize({ ptrt, wordty, wordty, ptrt }); + else if(type->isRangeType()) return getAggregateSize({ wordty, wordty, wordty }); + else if(type->isPointerType() || type->isFunctionType() || type->isNullType()) + { + return getSizeOfType(wordty); + } else if(type->isArrayType()) { return type->toArrayType()->getArraySize() * getSizeOfType(type->getArrayElementType()); } else if(type->isEnumType()) { - return getAggregateSize({ i64t, type->toEnumType()->getCaseType() }); + return getAggregateSize({ wordty, type->toEnumType()->getCaseType() }); } else if(type->isAnyType()) { - return getAggregateSize({ i64t, ptrt, fir::ArrayType::get(fir::Type::getInt8(), BUILTIN_ANY_DATA_BYTECOUNT) }); + return getAggregateSize({ wordty, ptrt, fir::ArrayType::get(fir::Type::getInt8(), BUILTIN_ANY_DATA_BYTECOUNT) }); } else if(type->isClassType() || type->isStructType() || type->isTupleType()) { @@ -1001,14 +1047,7 @@ namespace fir if(type->isClassType()) { - auto c = type->toClassType(); - auto base = c; - while(base) - { - tys.insert(tys.begin(), base->getElements().begin(), base->getElements().end()); - base = base->getBaseClass(); - } - + tys = type->toClassType()->getAllElementsIncludingBase(); tys.insert(tys.begin(), fir::Type::getInt8Ptr()); } else if(type->isStructType()) @@ -1035,11 +1074,11 @@ namespace fir if(maxSz > 0) { - return getAggregateSize({ Type::getInt64(), ArrayType::get(Type::getInt8(), maxSz) }); + return getAggregateSize({ wordty, ArrayType::get(Type::getInt8(), maxSz) }); } else { - return getAggregateSize({ Type::getInt64() }); + return getAggregateSize({ wordty }); } } else if(type->isRawUnionType()) @@ -1057,6 +1096,10 @@ namespace fir { return getSizeOfType(type->toUnionVariantType()->getInteriorType()); } + else if(type->isOpaqueType()) + { + return type->toOpaqueType()->getTypeSizeInBits() / 8; + } else { error("cannot get size of unsupported type '%s'", type); diff --git a/source/fir/Types/UnionType.cpp b/source/fir/Types/UnionType.cpp index 0e429539..e5bd4489 100644 --- a/source/fir/Types/UnionType.cpp +++ b/source/fir/Types/UnionType.cpp @@ -1,5 +1,5 @@ // UnionType.cpp -// Copyright (c) 2014 - 2016, zhiayang@gmail.com +// Copyright (c) 2014 - 2016, zhiayang // Licensed under the Apache License Version 2.0. #include "errors.h" @@ -79,7 +79,7 @@ namespace fir error("no variant with name '%s'", name); } - util::hash_map UnionType::getVariants() + const util::hash_map& UnionType::getVariants() { return this->variants; } diff --git a/source/fir/Value.cpp b/source/fir/Value.cpp index 7e8eb721..42ebab43 100644 --- a/source/fir/Value.cpp +++ b/source/fir/Value.cpp @@ -1,5 +1,5 @@ // Value.cpp -// Copyright (c) 2014 - 2016, zhiayang@gmail.com +// Copyright (c) 2014 - 2016, zhiayang // Licensed under the Apache License Version 2.0. #include "ir/value.h" @@ -17,7 +17,7 @@ namespace fir { if(this->valueType) return this->valueType; - error("Value has no type????"); + error("value has no type????"); } bool Value::hasName() @@ -40,6 +40,10 @@ namespace fir return this->ident; } + size_t Value::getCurrentValueId() + { + return vnames; + } diff --git a/source/fir/interp/compiler.cpp b/source/fir/interp/compiler.cpp new file mode 100644 index 00000000..c60bb2d0 --- /dev/null +++ b/source/fir/interp/compiler.cpp @@ -0,0 +1,83 @@ +// compiler.cpp +// Copyright (c) 2017, zhiayang@gmail.com +// Licensed under the Apache License Version 2.0. + +#include "ir/type.h" +#include "ir/value.h" +#include "ir/interp.h" +#include "ir/module.h" +#include "ir/function.h" +#include "ir/instruction.h" + +namespace fir { +namespace interp +{ + static interp::Instruction compileInstruction(InterpState* is, fir::Function* parent, fir::Instruction* finstr) + { + iceAssert(finstr); + + interp::Instruction ret; + + ret.result = finstr->realOutput; + ret.opcode = (uint64_t) finstr->opKind; + + for(auto a : finstr->operands) + ret.args.push_back(a); + + return ret; + } + + static interp::Block compileBlock(InterpState* is, fir::Function* parent, fir::IRBlock* fib) + { + iceAssert(fib); + + interp::Block ret; + ret.blk = fib; + ret.instructions = util::map(fib->getInstructions(), [is, parent](fir::Instruction* i) -> interp::Instruction { + return compileInstruction(is, parent, i); + }); + + return ret; + } + + interp::Function& InterpState::compileFunction(fir::Function* fn) + { + iceAssert(fn); + + interp::Function ret; + ret.func = fn; + + ret.blocks = util::map(fn->getBlockList(), [fn, this](fir::IRBlock* b) -> interp::Block { + return compileBlock(this, fn, b); + }); + + if(fn->isCStyleVarArg()) + iceAssert(ret.blocks.empty()); + + if(ret.blocks.empty()) + ret.isExternal = true, ret.extFuncName = fn->getName().name; + + + // add it. + this->compiledFunctions[ret.func] = ret; + return this->compiledFunctions[ret.func]; + } +} +} + + + + + + + + + + + + + + + + + diff --git a/source/fir/interp/interpreter.cpp b/source/fir/interp/interpreter.cpp new file mode 100644 index 00000000..b7108a4b --- /dev/null +++ b/source/fir/interp/interpreter.cpp @@ -0,0 +1,2551 @@ +// interpreter.cpp +// Copyright (c) 2017, zhiayang@gmail.com +// Licensed under the Apache License Version 2.0. + +#include "ir/type.h" +#include "ir/value.h" +#include "ir/interp.h" +#include "ir/module.h" +#include "ir/function.h" +#include "ir/instruction.h" + +#include "gluecode.h" +#include "platform.h" + +#define FFI_BUILDING +#include + +#define LARGE_DATA_SIZE 32 + +#define SLICE_DATA_INDEX 0 +#define SLICE_LENGTH_INDEX 1 + +#define SAA_DATA_INDEX 0 +#define SAA_LENGTH_INDEX 1 +#define SAA_CAPACITY_INDEX 2 +#define SAA_REFCOUNTPTR_INDEX 3 + +#define ANY_TYPEID_INDEX 0 +#define ANY_REFCOUNTPTR_INDEX 1 +#define ANY_DATA_ARRAY_INDEX 2 + + +#ifdef _MSC_VER + #pragma warning(push, 0) + #pragma warning(disable: 4018) +#else + /* + so msvc doesn't really warn on this. we disable these warnings locally, because of the way we're doing the + operations. we can guarantee (because IRBuilder says so) that we won't be doing strange things like + bitwise-not-ing a boolean or left-shifting a floating point, but the compiler won't know so we just ignore + these things to clean up the output. + */ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wpragmas" + #pragma GCC diagnostic ignored "-Wunknown-warning-option" + + #pragma GCC diagnostic ignored "-Wbool-operation" + #pragma GCC diagnostic ignored "-Wint-in-bool-context" + #pragma GCC diagnostic ignored "-Wimplicit-conversion-floating-point-to-bool" + #pragma GCC diagnostic ignored "-Wdelete-incomplete" +#endif + + + +namespace fir { +namespace interp +{ + //! ACHTUNG ! + //* in the interpreter, we assume all structs are packed, and there are no padding/alignment bytes anywhere. + //* this greatly simplifies everything, and the performance impact is probably insignificant next to the (power of the force) + //* whole interpreter anyway. + + + template + static interp::Value makeValue(fir::Value* fv, const T& val) + { + interp::Value ret; + ret.val = fv; + ret.type = fv->getType(); + ret.dataSize = sizeof(T); + + if(auto fsz = getSizeOfType(ret.type); fsz != sizeof(T)) + error("packing error of type '%s': predicted size %d, actual size %d!", ret.type, fsz, sizeof(T)); + + memset(&ret.data[0], 0, LARGE_DATA_SIZE); + + if(sizeof(T) > LARGE_DATA_SIZE) + { + ret.ptr = malloc(sizeof(T)); + memmove(ret.ptr, &val, sizeof(T)); + } + else + { + memmove(&ret.data[0], &val, sizeof(T)); + } + + return ret; + } + + + // this lets us specify the type, instead of using the one in the Value + static interp::Value makeValueOfType(fir::Value* fv, fir::Type* ty) + { + interp::Value ret; + ret.val = fv; + ret.type = ty; + ret.dataSize = getSizeOfType(ret.type); + + memset(&ret.data[0], 0, LARGE_DATA_SIZE); + + if(ret.dataSize > LARGE_DATA_SIZE) + ret.ptr = calloc(1, ret.dataSize); + + return ret; + } + + + interp::Value InterpState::makeValue(fir::Value* fv) + { + return makeValueOfType(fv, fv->getType()); + } + + + template + static T getActualValue(const interp::Value& v) + { + if(v.dataSize > LARGE_DATA_SIZE) + { + return *((T*) v.ptr); + } + else + { + return *((T*) &v.data[0]); + } + } + + + static interp::Value cloneValue(const interp::Value& v) + { + interp::Value ret = v; + + if(v.dataSize > LARGE_DATA_SIZE) + { + ret.ptr = calloc(1, v.dataSize); + memmove(ret.ptr, v.ptr, v.dataSize); + } + return ret; + } + + static interp::Value cloneValue(fir::Value* fv, const interp::Value& v) + { + interp::Value ret = v; + ret.val = fv; + ret.globalValTracker = v.globalValTracker; + + if(v.dataSize > LARGE_DATA_SIZE) + { + ret.ptr = calloc(1, v.dataSize); + memmove(ret.ptr, v.ptr, v.dataSize); + } + return ret; + } + + static void setValueRaw(InterpState* is, interp::Value* target, void* value, size_t sz) + { + if(target->dataSize != sz) + error("interp: cannot set value, size mismatch (%zu vs %zu)", target->dataSize, sz); + + if(sz > LARGE_DATA_SIZE) memmove(target->ptr, value, sz); + else memmove(&target->data[0], value, sz); + } + + static void setValue(InterpState* is, interp::Value* target, const interp::Value& val) + { + if(target->type != val.type) + error("interp: cannot set value, conflicting types '%s' and '%s'", target->type, val.type); + + if(val.dataSize > LARGE_DATA_SIZE) + memmove(target->ptr, val.ptr, val.dataSize); + + else + memmove(&target->data[0], &val.data[0], val.dataSize); + } + + static char* makeGlobalString(InterpState* is, const std::string& str) + { + auto s = new char[str.size() + 1]; + memmove(s, str.c_str(), str.size()); + s[str.size()] = 0; + + is->strings.push_back(s); + + return s; + } + + static interp::Value loadFromPtr(const interp::Value& x, fir::Type* ty) + { + auto ptr = (void*) getActualValue(x); + + interp::Value ret; + ret.dataSize = getSizeOfType(ty); + ret.type = ty; + + if(ret.dataSize > LARGE_DATA_SIZE) + { + // clone the memory and store it. + auto newmem = malloc(ret.dataSize); + memmove(newmem, ptr, ret.dataSize); + ret.ptr = newmem; + } + else + { + // memcopy. + memmove(&ret.data[0], ptr, ret.dataSize); + } + + return ret; + } + + + static std::map cachedConstants; + static interp::Value makeConstant(InterpState* is, ConstantValue* c) + { + auto constructStructThingy2 = [](fir::Value* val, size_t datasize, const std::vector& inserts) -> interp::Value { + + uint8_t* buffer = 0; + + interp::Value ret; + ret.dataSize = datasize; + ret.type = val->getType(); + ret.val = val; + + if(datasize > LARGE_DATA_SIZE) { buffer = new uint8_t[datasize]; ret.ptr = buffer; } + else { buffer = &ret.data[0]; } + + iceAssert(buffer); + + uint8_t* ofs = buffer; + for(const auto& v : inserts) + { + if(v.dataSize > LARGE_DATA_SIZE) memmove(ofs, v.ptr, v.dataSize); + else memmove(ofs, &v.data[0], v.dataSize); + + ofs += v.dataSize; + } + + return ret; + }; + + auto constructStructThingy = [is, &constructStructThingy2](fir::Value* val, size_t datasize, + const std::vector& inserts) -> interp::Value + { + std::vector vals; + for(const auto& x : inserts) + vals.push_back(makeConstant(is, x)); + + return constructStructThingy2(val, datasize, vals); + }; + + + if(auto ci = dcast(fir::ConstantInt, c)) + { + interp::Value ret; + + if(ci->getType() == fir::Type::getInt8()) ret = makeValue(c, (int8_t) ci->getSignedValue()); + else if(ci->getType() == fir::Type::getInt16()) ret = makeValue(c, (int16_t) ci->getSignedValue()); + else if(ci->getType() == fir::Type::getInt32()) ret = makeValue(c, (int32_t) ci->getSignedValue()); + else if(ci->getType() == fir::Type::getInt64()) ret = makeValue(c, (int64_t) ci->getSignedValue()); + else if(ci->getType() == fir::Type::getUint8()) ret = makeValue(c, (uint8_t) ci->getUnsignedValue()); + else if(ci->getType() == fir::Type::getUint16()) ret = makeValue(c, (uint16_t) ci->getUnsignedValue()); + else if(ci->getType() == fir::Type::getUint32()) ret = makeValue(c, (uint32_t) ci->getUnsignedValue()); + else if(ci->getType() == fir::Type::getUint64()) ret = makeValue(c, (uint64_t) ci->getUnsignedValue()); + else error("interp: unsupported type '%s' for integer constant", ci->getType()); + + return (cachedConstants[c] = ret); + } + else if(auto cf = dcast(fir::ConstantFP, c)) + { + return cachedConstants[c] = makeValue(c, cf->getValue()); + } + else if(auto cb = dcast(fir::ConstantBool, c)) + { + return cachedConstants[c] = makeValue(c, cb->getValue()); + } + else if(auto cs = dcast(fir::ConstantString, c)) + { + auto str = cs->getValue(); + + interp::Value ret; + ret.dataSize = sizeof(char*); + ret.type = cs->getType(); + ret.val = cs; + + auto s = makeGlobalString(is, str); + + setValueRaw(is, &ret, &s, sizeof(char*)); + + return (cachedConstants[c] = ret); + } + else if(auto cbc = dcast(fir::ConstantBitcast, c)) + { + auto thing = makeConstant(is, cbc->getValue()); + auto ret = cloneValue(cbc, thing); + + return (cachedConstants[c] = ret); + } + else if(auto ca = dcast(fir::ConstantArray, c)) + { + auto bytecount = ca->getValues().size() * getSizeOfType(ca->getType()->getArrayElementType()); + + auto ret = constructStructThingy(ca, bytecount, ca->getValues()); + return (cachedConstants[c] = ret); + } + else if(auto ct = dcast(fir::ConstantTuple, c)) + { + size_t bytecount = 0; + for(auto t : ct->getValues()) + bytecount += getSizeOfType(t->getType()); + + auto ret = constructStructThingy(ct, bytecount, ct->getValues()); + return (cachedConstants[c] = ret); + } + else if(auto cec = dcast(fir::ConstantEnumCase, c)) + { + auto bytecount = getSizeOfType(cec->getIndex()->getType()) + getSizeOfType(cec->getValue()->getType()); + + auto ret = constructStructThingy(cec, bytecount, { cec->getIndex(), cec->getValue() }); + return (cachedConstants[c] = ret); + } + else if(auto cas = dcast(fir::ConstantArraySlice, c)) + { + auto ptr = cas->getData(); + auto len = cas->getLength(); + + auto bytecount = getSizeOfType(ptr->getType()) + getSizeOfType(len->getType()); + auto ret = constructStructThingy(cas, bytecount, { ptr, len }); + + return (cachedConstants[c] = ret); + } + else if(auto cda = dcast(fir::ConstantDynamicArray, c)) + { + std::vector mems; + auto bytecount = getSizeOfType(cda->getType()); + + if(cda->getArray()) + { + auto theArray = cda->getArray(); + auto sz = getSizeOfType(theArray->getType()); + + void* buffer = new uint8_t[sz]; memset(buffer, 0, sz); + is->globalAllocs.push_back(buffer); + + uint8_t* ofs = (uint8_t*) buffer; + for(const auto& x : theArray->getValues()) + { + auto v = makeConstant(is, x); + + if(v.dataSize > LARGE_DATA_SIZE) memmove(ofs, v.ptr, v.dataSize); + else memmove(ofs, &v.data[0], v.dataSize); + + ofs += v.dataSize; + } + + interp::Value fakeptr; + fakeptr.val = 0; + fakeptr.type = cda->getType()->getArrayElementType()->getMutablePointerTo(); + fakeptr.dataSize = sizeof(void*); + + setValueRaw(is, &fakeptr, &buffer, sizeof(void*)); + + mems = { + fakeptr, makeConstant(is, fir::ConstantInt::getNative(theArray->getValues().size())), + makeConstant(is, fir::ConstantInt::getNative(-1)), makeConstant(is, fir::ConstantInt::getNative(0)) + }; + } + else + { + mems = { + makeConstant(is, cda->getData()), makeConstant(is, cda->getLength()), + makeConstant(is, cda->getCapacity()), makeConstant(is, fir::ConstantInt::getNative(0)) + }; + } + + auto ret = constructStructThingy2(cda, bytecount, mems); + return (cachedConstants[c] = ret); + } + else if(auto fn = dcast(fir::Function, c)) + { + // ok -- when we get a "function" as a constant, what really happened in the source code is that we referred to a function + // by name, without calling it. we expect, then, to get a function pointer out of it. + + // the problem is, we can also call function pointers that come from raw pointers that point to *real* CPU instructions + // somewhere --- nobody is stopping the program from calling dlsym() or whatever and calling a function using + // that pointer. + + // so, in order to support that, we return the fir::Function itself as a pointer. when we do the pointer-call, the + // stub function will check the list of functions to see if it was a source-originated function, and if so call it + // normally. if not, then we will use libffi to call it. + + // make sure we compile it first, so it gets added to InterpState::compiledFunctions + is->compileFunction(fn); + + auto ret = makeValue(fn, (uintptr_t) fn); + return (cachedConstants[c] = ret); + } + else if(auto glob = dcast(fir::GlobalValue, c)) + { + if(auto it = is->globals.find(c); it != is->globals.end()) + return it->second.first; + + else + error("interp: global value '%s' id %zu was not found", glob->getName().str(), glob->id); + } + else + { + auto ret = is->makeValue(c); + return (cachedConstants[c] = ret); + } + } + + + + InterpState::InterpState(Module* mod) : module(mod) + { + } + + InterpState::~InterpState() + { + for(void* p : this->globalAllocs) + delete[] p; + } + + void InterpState::initialise() + { + for(const auto [ str, glob ] : this->module->_getGlobalStrings()) + { + auto val = makeValue(glob); + auto s = makeGlobalString(this, str); + + setValueRaw(this, &val, &s, sizeof(char*)); + + val.globalValTracker = glob; + this->globals[glob] = { val, false }; + } + + for(const auto [ id, glob ] : this->module->_getGlobals()) + { + auto ty = glob->getType(); + auto sz = getSizeOfType(ty); + + void* buffer = new uint8_t[sz]; + memset(buffer, 0, sz); + + this->globalAllocs.push_back(buffer); + + if(auto init = glob->getInitialValue(); init) + { + auto x = makeConstant(this, init); + if(x.dataSize > LARGE_DATA_SIZE) memmove(buffer, x.ptr, x.dataSize); + else memmove(buffer, &x.data[0], x.dataSize); + } + + auto ret = makeValueOfType(glob, ty->getPointerTo()); + setValueRaw(this, &ret, &buffer, sizeof(void*)); + + ret.globalValTracker = glob; + this->globals[glob] = { ret, false }; + } + + for(auto intr : this->module->_getIntrinsicFunctions()) + { + auto fname = Identifier("__interp_intrinsic_" + intr.first.str(), IdKind::Name); + auto fn = this->module->getOrCreateFunction(fname, intr.second->getType(), fir::LinkageType::ExternalWeak); + + // interp::compileFunction already maps the newly compiled interp::Function, but since we created a + // new function here `fn` that doesn't match the intrinsic function `intr`, we need to map that as well. + this->compiledFunctions[intr.second] = this->compileFunction(fn); + } + + + #ifdef _WIN32 + + // ok, this is mostly for windows --- printf and friends are not *real* functions, but rather + // macros defined in stdio.h, or something like that. so, we make "intrinsic wrappers" that call + // it in the interpreter, which we dlopen into the context anyway. + { + std::vector names = { + "printf", + "sprintf", + "snprintf", + "fprintf" + }; + + for(const auto& name : names) + { + auto fn = this->module->getFunction(Identifier(name, IdKind::Name)); + if(fn) + { + auto wrapper = this->module->getOrCreateFunction(Identifier("__interp_wrapper_" + name, IdKind::Name), + fn->getType()->toFunctionType(), fir::LinkageType::ExternalWeak); + + this->compiledFunctions[fn] = this->compileFunction(wrapper); + } + } + } + #endif + } + + + // the purpose of this function is to write-back any changes we made to globals while interpreting, + // back to the "value" of the global -- mainly for compile-time execution, so modifications will + // propagate to the backend code generation. + void InterpState::finalise() + { + for(const auto [ id, glob ] : this->module->_getGlobals()) + { + // by right we are not supposed to add (or even change the FIR module at all) between calling + // initialise() and finalise(), but be defensive a bit. + if(auto it = this->globals.find(glob); it != this->globals.end()) + { + // only write-back if we modified the global. + if(it->second.second) + { + auto val = loadFromPtr(it->second.first, it->first->getType()); + glob->setInitialValue(this->unwrapInterpValueIntoConstant(val)); + } + } + } + } + + + + static ffi_type* convertTypeToLibFFI(fir::Type* ty) + { + if(ty->isPointerType()) + { + return &ffi_type_pointer; + } + else if(ty->isBoolType()) + { + //? HMMM.... + return &ffi_type_uint8; + } + else if(ty->isVoidType()) + { + return &ffi_type_void; + } + else if(ty->isIntegerType()) + { + if(ty == Type::getInt8()) return &ffi_type_sint8; + if(ty == Type::getInt16()) return &ffi_type_sint16; + if(ty == Type::getInt32()) return &ffi_type_sint32; + if(ty == Type::getInt64()) return &ffi_type_sint64; + + if(ty == Type::getUint8()) return &ffi_type_uint8; + if(ty == Type::getUint16()) return &ffi_type_uint16; + if(ty == Type::getUint32()) return &ffi_type_uint32; + if(ty == Type::getUint64()) return &ffi_type_uint64; + } + else if(ty->isFloatingPointType()) + { + if(ty == Type::getFloat32()) return &ffi_type_float; + if(ty == Type::getFloat64()) return &ffi_type_double; + } + else + { + + } + + error("interp: unsupported type '%s' in libffi-translation", ty); + } + + + + + + + + + static std::vector getTypeListOfType(fir::Type* ty) + { + if(ty->isStructType()) + { + return ty->toStructType()->getElements(); + } + else if(ty->isClassType()) + { + return ((fir::Type*) fir::Type::getInt8Ptr() + ty->toClassType()->getAllElementsIncludingBase()); + } + else if(ty->isTupleType()) + { + return ty->toTupleType()->getElements(); + } + else if(ty->isArraySliceType()) + { + if(ty->toArraySliceType()->isMutable()) + return { ty->getArrayElementType()->getMutablePointerTo(), fir::Type::getNativeWord() }; + + else + return { ty->getArrayElementType()->getPointerTo(), fir::Type::getNativeWord() }; + } + else if(ty->isAnyType()) + { + return { + fir::Type::getNativeUWord(), fir::Type::getNativeWordPtr(), + fir::ArrayType::get(fir::Type::getInt8(), BUILTIN_ANY_DATA_BYTECOUNT) + }; + } + else if(ty->isRangeType()) + { + return { + fir::Type::getNativeWord(), fir::Type::getNativeWord(), fir::Type::getNativeWord() + }; + } + else if(ty->isEnumType()) + { + return { + fir::Type::getNativeWord(), ty->toEnumType()->getCaseType() + }; + } + else if(ty->isStringType() || ty->isDynamicArrayType()) + { + std::vector mems(4); + + if(ty->isDynamicArrayType()) mems[SAA_DATA_INDEX] = ty->getArrayElementType()->getMutablePointerTo(); + else mems[SAA_DATA_INDEX] = fir::Type::getMutInt8Ptr(); + + mems[SAA_LENGTH_INDEX] = fir::Type::getNativeWord(); + mems[SAA_CAPACITY_INDEX] = fir::Type::getNativeWord(); + mems[SAA_REFCOUNTPTR_INDEX] = fir::Type::getNativeWordPtr(); + + return mems; + } + else + { + error("interp: unsupported type '%s' for insert/extractvalue", ty); + } + } + + static interp::Value doInsertValue(interp::InterpState* is, fir::Value* res, const interp::Value& str, const interp::Value& elm, size_t idx) + { + // we clone the value first + auto ret = cloneValue(res, str); + + size_t ofs = 0; + + if(str.type->isArrayType()) + { + auto arrty = str.type->toArrayType(); + iceAssert(idx < arrty->getArraySize()); + + ofs = idx * getSizeOfType(arrty->getElementType()); + } + else if(str.type->isUnionType()) + { + // we only support getting the id with insert/extractvalue. + iceAssert(idx == 0); + ofs = 0; + } + else + { + auto typelist = getTypeListOfType(str.type); + + iceAssert(idx < typelist.size()); + + for(size_t i = 0; i < idx; i++) + ofs += getSizeOfType(typelist[i]); + } + + uintptr_t dst = 0; + if(str.dataSize > LARGE_DATA_SIZE) dst = (uintptr_t) ret.ptr; + else dst = (uintptr_t) &ret.data[0]; + + uintptr_t src = 0; + if(elm.dataSize > LARGE_DATA_SIZE) src = (uintptr_t) elm.ptr; + else src = (uintptr_t) &elm.data[0]; + + memmove((void*) (dst + ofs), (void*) src, elm.dataSize); + + return ret; + } + + + static interp::Value doExtractValue(interp::InterpState* is, fir::Value* res, const interp::Value& str, size_t idx) + { + size_t ofs = 0; + + fir::Type* elm = 0; + if(str.type->isArrayType()) + { + auto arrty = str.type->toArrayType(); + iceAssert(idx < arrty->getArraySize()); + + ofs = idx * getSizeOfType(arrty->getElementType()); + elm = arrty->getElementType(); + } + else if(str.type->isUnionType()) + { + // we only support getting the id with insert/extractvalue. + iceAssert(idx == 0); + elm = fir::Type::getNativeWord(); + ofs = 0; + } + else + { + auto typelist = getTypeListOfType(str.type); + + iceAssert(idx < typelist.size()); + + for(size_t i = 0; i < idx; i++) + ofs += getSizeOfType(typelist[i]); + + elm = typelist[idx]; + } + + auto ret = is->makeValue(res); + iceAssert(ret.type == elm); + + uintptr_t src = 0; + if(str.dataSize > LARGE_DATA_SIZE) src = (uintptr_t) str.ptr; + else src = (uintptr_t) &str.data[0]; + + uintptr_t dst = 0; + if(ret.dataSize > LARGE_DATA_SIZE) dst = (uintptr_t) ret.ptr; + else dst = (uintptr_t) &ret.data[0]; + + memmove((void*) dst, (void*) (src + ofs), ret.dataSize); + + return ret; + } + + // this saves us a lot of copy/paste + + template + static interp::Value oneArgumentOpIntOnly(InterpState* is, fir::Type* resty, const interp::Value& a, Functor op) + { + auto ty = a.type; + + interp::Value res; + res.dataSize = getSizeOfType(resty); + res.type = resty; + + if(ty == Type::getInt8()) { auto tmp = op(getActualValue(a)); setValueRaw(is, &res, &tmp, sizeof(tmp)); return res; } + if(ty == Type::getInt16()) { auto tmp = op(getActualValue(a)); setValueRaw(is, &res, &tmp, sizeof(tmp)); return res; } + if(ty == Type::getInt32()) { auto tmp = op(getActualValue(a)); setValueRaw(is, &res, &tmp, sizeof(tmp)); return res; } + if(ty == Type::getInt64()) { auto tmp = op(getActualValue(a)); setValueRaw(is, &res, &tmp, sizeof(tmp)); return res; } + if(ty == Type::getUint8()) { auto tmp = op(getActualValue(a)); setValueRaw(is, &res, &tmp, sizeof(tmp)); return res; } + if(ty == Type::getUint16()) { auto tmp = op(getActualValue(a)); setValueRaw(is, &res, &tmp, sizeof(tmp)); return res; } + if(ty == Type::getUint32()) { auto tmp = op(getActualValue(a)); setValueRaw(is, &res, &tmp, sizeof(tmp)); return res; } + if(ty == Type::getUint64()) { auto tmp = op(getActualValue(a)); setValueRaw(is, &res, &tmp, sizeof(tmp)); return res; } + if(ty->isPointerType()) { auto tmp = op(getActualValue(a)); setValueRaw(is, &res, &tmp, sizeof(tmp)); return res; } + if(ty->isBoolType()) { auto tmp = op(getActualValue(a)); setValueRaw(is, &res, &tmp, sizeof(tmp)); return res; } + else error("interp: unsupported type '%s'", ty); + } + + template + static interp::Value oneArgumentOpIntOnly(InterpState* is, const interp::Instruction& inst, const interp::Value& a, Functor op) + { + auto ret = oneArgumentOpIntOnly(is, inst.result->getType(), a, op); + ret.val = inst.result; + + return ret; + } + + template + static interp::Value oneArgumentOp(InterpState* is, fir::Type* resty, const interp::Value& a, Functor op) + { + auto ty = a.type; + + if(!ty->isFloatingPointType()) + return oneArgumentOpIntOnly(is, resty, a, op); + + interp::Value res; + res.dataSize = getSizeOfType(resty); + res.type = resty; + + if(ty == Type::getFloat32()) { auto tmp = op(getActualValue(a)); setValueRaw(is, &res, &tmp, sizeof(tmp)); return res; } + if(ty == Type::getFloat64()) { auto tmp = op(getActualValue(a)); setValueRaw(is, &res, &tmp, sizeof(tmp)); return res; } + else error("interp: unsupported type '%s'", ty); + } + + template + static interp::Value oneArgumentOp(InterpState* is, const interp::Instruction& inst, const interp::Value& a, Functor op) + { + auto ret = oneArgumentOp(is, inst.result->getType(), a, op); + ret.val = inst.result; + + return ret; + } + + + + template + static interp::Value twoArgumentOpIntOnly(InterpState* is, fir::Type* resty, const interp::Value& a, + const interp::Value& b, Functor op) + { + auto aty = a.type; + auto bty = b.type; + + using i8tT = int8_t; auto i8t = Type::getInt8(); + using i16tT = int16_t; auto i16t = Type::getInt16(); + using i32tT = int32_t; auto i32t = Type::getInt32(); + using i64tT = int64_t; auto i64t = Type::getInt64(); + using u8tT = uint8_t; auto u8t = Type::getUint8(); + using u16tT = uint16_t; auto u16t = Type::getUint16(); + using u32tT = uint32_t; auto u32t = Type::getUint32(); + using u64tT = uint64_t; auto u64t = Type::getUint64(); + + #define gav(t, x) getActualValue(x) + + interp::Value res; + res.dataSize = getSizeOfType(resty); + res.type = resty; + + #define If(at, bt) do { if(aty == (at) && bty == (bt)) { \ + auto tmp = op(gav(at##T, a), gav(bt##T, b)); \ + setValueRaw(is, &res, &tmp, sizeof(tmp)); \ + return res; \ + } } while(0) + + // FUCK LAH + If(i8t, i8t); If(i8t, i16t); If(i8t, i32t); If(i8t, i64t); + If(i16t, i8t); If(i16t, i16t); If(i16t, i32t); If(i16t, i64t); + If(i32t, i8t); If(i32t, i16t); If(i32t, i32t); If(i32t, i64t); + If(i64t, i8t); If(i64t, i16t); If(i64t, i32t); If(i64t, i64t); + + If(u8t, u8t); If(u8t, u16t); If(u8t, u32t); If(u8t, u64t); + If(u16t, u8t); If(u16t, u16t); If(u16t, u32t); If(u16t, u64t); + If(u32t, u8t); If(u32t, u16t); If(u32t, u32t); If(u32t, u64t); + If(u64t, u8t); If(u64t, u16t); If(u64t, u32t); If(u64t, u64t); + + if(aty->isPointerType() && bty->isPointerType()) + { + auto tmp = op(gav(uintptr_t, a), gav(uintptr_t, b)); + setValueRaw(is, &res, &tmp, sizeof(tmp)); + return res; + } + if(aty->isBoolType() && bty->isBoolType()) + { + auto tmp = op(gav(bool, a), gav(bool, b)); + setValueRaw(is, &res, &tmp, sizeof(tmp)); + return res; + } + + error("interp: unsupported types '%s' and '%s' for arithmetic", aty, bty); + + #undef If + #undef gav + } + + + template + static interp::Value twoArgumentOp(InterpState* is, fir::Type* resty, const interp::Value& a, + const interp::Value& b, Functor op) + { + if(!(a.type->isFloatingPointType() || b.type->isFloatingPointType())) + return twoArgumentOpIntOnly(is, resty, a, b, op); + + auto aty = a.type; + auto bty = b.type; + + using i8tT = int8_t; auto i8t = Type::getInt8(); + using i16tT = int16_t; auto i16t = Type::getInt16(); + using i32tT = int32_t; auto i32t = Type::getInt32(); + using i64tT = int64_t; auto i64t = Type::getInt64(); + using u8tT = uint8_t; auto u8t = Type::getUint8(); + using u16tT = uint16_t; auto u16t = Type::getUint16(); + using u32tT = uint32_t; auto u32t = Type::getUint32(); + using u64tT = uint64_t; auto u64t = Type::getUint64(); + using f32tT = float; auto f32t = Type::getFloat32(); + using f64tT = double; auto f64t = Type::getFloat64(); + + #define gav(t, x) getActualValue(x) + + interp::Value res; + res.dataSize = getSizeOfType(resty); + res.type = resty; + + #define If(at, bt) do { if(aty == (at) && bty == (bt)) { \ + auto tmp = op(gav(at##T, a), gav(bt##T, b)); \ + setValueRaw(is, &res, &tmp, sizeof(tmp)); \ + return res; \ + } } while(0) + + // FUCK LAH + If(i8t, f32t); If(i8t, f64t); If(u8t, f32t); If(u8t, f64t); + If(i16t, f32t); If(i16t, f64t); If(u16t, f32t); If(u16t, f64t); + If(i32t, f32t); If(i32t, f64t); If(u32t, f32t); If(u32t, f64t); + If(i64t, f32t); If(i64t, f64t); If(u64t, f32t); If(u64t, f64t); + + If(f32t, i8t); If(f64t, i8t); If(f32t, u8t); If(f64t, u8t); + If(f32t, i16t); If(f64t, i16t); If(f32t, u16t); If(f64t, u16t); + If(f32t, i32t); If(f64t, i32t); If(f32t, u32t); If(f64t, u32t); + If(f32t, i64t); If(f64t, i64t); If(f32t, u64t); If(f64t, u64t); + + If(f32t, f32t); If(f32t, f64t); If(f64t, f32t); If(f64t, f64t); + + #undef If + #undef gav + + error("interp: unsupported types '%s' and '%s'", aty, bty); + } + + + + + template + static interp::Value twoArgumentOpIntOnly(InterpState* is, const interp::Instruction& inst, const interp::Value& a, + const interp::Value& b, Functor op) + { + auto ret = twoArgumentOpIntOnly(is, inst.result->getType(), a, b, op); + ret.val = inst.result; + + return ret; + } + + template + static interp::Value twoArgumentOp(InterpState* is, const interp::Instruction& inst, const interp::Value& a, + const interp::Value& b, Functor op) + { + auto ret = twoArgumentOp(is, inst.result->getType(), a, b, op); + ret.val = inst.result; + + return ret; + } + + + + + + + + static interp::Value runFunctionWithLibFFI(InterpState* is, void* fnptr, fir::FunctionType* fnty, const std::vector& args) + { + // we are assuming the values in 'args' are correct! + ffi_type** arg_types = new ffi_type*[args.size()]; + { + std::vector tmp; + for(size_t i = 0; i < args.size(); i++) + { + tmp.push_back(convertTypeToLibFFI(args[i].type)); + arg_types[i] = tmp[i]; + } + } + + ffi_type* ffi_retty = 0; + ffi_cif fn_cif; + { + ffi_retty = convertTypeToLibFFI(fnty->getReturnType()); + + if(args.size() > fnty->getArgumentTypes().size()) + { + iceAssert(fnty->isCStyleVarArg()); + auto st = ffi_prep_cif_var(&fn_cif, FFI_DEFAULT_ABI, fnty->getArgumentTypes().size(), args.size(), ffi_retty, arg_types); + if(st != FFI_OK) + error("interp: ffi_prep_cif_var failed! (%d)", st); + } + else + { + auto st = ffi_prep_cif(&fn_cif, FFI_DEFAULT_ABI, args.size(), ffi_retty, arg_types); + if(st != FFI_OK) + error("interp: ffi_prep_cif failed! (%d)", st); + } + } + + void** arg_pointers = new void*[args.size()]; + { + void** arg_values = new void*[args.size()]; + + // because this thing is dumb + for(size_t i = 0; i < args.size(); i++) + { + if(args[i].dataSize <= LARGE_DATA_SIZE) + arg_values[i] = (void*) &args[i].data[0]; + } + + for(size_t i = 0; i < args.size(); i++) + { + if(args[i].dataSize <= LARGE_DATA_SIZE) + arg_pointers[i] = (void*) arg_values[i]; + + else + arg_pointers[i] = (void*) args[i].ptr; + } + + delete[] arg_values; + } + + void* ret_buffer = new uint8_t[std::max(ffi_retty->size, (size_t) 8)]; + ffi_call(&fn_cif, FFI_FN(fnptr), ret_buffer, arg_pointers); + + interp::Value ret; + ret.type = fnty->getReturnType(); + ret.dataSize = ffi_retty->size; + + setValueRaw(is, &ret, ret_buffer, ret.dataSize); + delete[] ret_buffer; + + delete[] arg_types; + delete[] arg_pointers; + + return ret; + } + + static interp::Value runFunctionWithLibFFI(InterpState* is, fir::Function* fn, const std::vector& args) + { + void* fnptr = platform::getSymbol(fn->getName().str()); + if(!fnptr) error("interp: failed to find symbol named '%s'\n", fn->getName().str()); + + return runFunctionWithLibFFI(is, fnptr, fn->getType(), args); + } + + static interp::Value runFunctionWithLibFFI(InterpState* is, const interp::Function& fn, const std::vector& args) + { + void* fnptr = platform::getSymbol(fn.func->getName().str()); + if(!fnptr) error("interp: failed to find symbol named '%s'\n", fn.func->getName().str()); + + return runFunctionWithLibFFI(is, fnptr, fn.func->getType(), args); + } + + + static const interp::Block* prepareFunctionToRun(InterpState* is, const interp::Function& fn, const std::vector& args) + { + iceAssert(args.size() == fn.func->getArgumentCount()); + + // when we start a function, clear the "stack frame". + is->stackFrames.push_back({ }); + + for(size_t i = 0; i < args.size(); i++) + { + auto farg = fn.func->getArguments()[i]; + is->stackFrames.back().values[farg] = cloneValue(farg, args[i]); + } + + iceAssert(!fn.blocks.empty()); + + auto entry = &fn.blocks[0]; + is->stackFrames.back().currentFunction = &fn; + is->stackFrames.back().currentBlock = entry; + is->stackFrames.back().previousBlock = 0; + + return entry; + } + + static void leaveFunction(InterpState* is) + { + auto frame = is->stackFrames.back(); + + for(void* alloca : frame.stackAllocs) + delete[] alloca; + + is->stackFrames.pop_back(); + } + + + + constexpr int FLOW_NORMAL = 0; + constexpr int FLOW_RETURN = 1; + constexpr int FLOW_BRANCH = 2; + constexpr int FLOW_FNCALL = 3; + constexpr int FLOW_DYCALL = 4; + + + struct InstrResult + { + // for ret + interp::Value returnValue; + + // for branches + const interp::Block* targetBlk = 0; + + // for calls + interp::Function* callTarget = 0; + std::vector callArguments; + fir::Value* callResultValue = 0; + + // for virtual calls. the args and resultvalue are shared. + interp::Value virtualCallTarget; + }; + + static int runInstruction(InterpState* is, const interp::Instruction& inst, InstrResult* instrRes); + + static interp::Value runBlock(InterpState* is, const interp::Block* blk) + { + for(size_t i = 0; i < blk->instructions.size();) + { + is->stackFrames.back().currentInstrIndex = i; + + InstrResult res; + int flow = runInstruction(is, blk->instructions[i], &res); + + switch(flow) + { + case FLOW_NORMAL: { + i += 1; + } break; + + case FLOW_BRANCH: { + is->stackFrames.back().previousBlock = blk; + is->stackFrames.back().currentBlock = res.targetBlk; + + blk = res.targetBlk; i = 0; + } break; + + case FLOW_FNCALL: { + if(res.callTarget->isExternal) + { + is->stackFrames.back().values[res.callResultValue] = runFunctionWithLibFFI(is, *res.callTarget, res.callArguments); + i += 1; + } + else + { + is->stackFrames.back().callResultOutput = res.callResultValue; + + auto newblk = prepareFunctionToRun(is, *res.callTarget, res.callArguments); + blk = newblk; i = 0; + } + } break; + + case FLOW_DYCALL: { + auto ptr = getActualValue(res.virtualCallTarget); + auto firfn = (fir::Function*) ptr; + + if(auto it = is->compiledFunctions.find(firfn); it != is->compiledFunctions.end()) + { + is->stackFrames.back().callResultOutput = res.callResultValue; + + auto newblk = prepareFunctionToRun(is, it->second, res.callArguments); + blk = newblk; i = 0; + } + else + { + auto targ = res.virtualCallTarget; + + // uwu. use libffi. + fir::FunctionType* fnty = 0; + if(targ.type->isFunctionType()) + fnty = targ.type->toFunctionType(); + + else if(targ.type->isPointerType() && targ.type->getPointerElementType()->isFunctionType()) + fnty = targ.type->getPointerElementType()->toFunctionType(); + + else + error("interp: call to function pointer with invalid type '%s'", targ.type); + + is->stackFrames.back().values[res.callResultValue] = runFunctionWithLibFFI(is, (void*) ptr, fnty, res.callArguments); + i += 1; + } + } break; + + case FLOW_RETURN: { + if(is->stackFrames.size() > 1) + { + leaveFunction(is); + auto& frm = is->stackFrames.back(); + + res.returnValue.val = frm.callResultOutput; + frm.values[frm.callResultOutput] = res.returnValue; + + blk = frm.currentBlock; + i = frm.currentInstrIndex + 1; + break; + } + else + { + return res.returnValue; + } + } + + default: { + error("interp: invalid flow state"); + } break; + } + } + + error("interp: invaild state"); + return interp::Value(); + } + + + + + interp::Value InterpState::runFunction(const interp::Function& fn, const std::vector& args) + { + auto ffn = fn.func; + if((!fn.func->isCStyleVarArg() && args.size() != fn.func->getArgumentCount()) + || (fn.func->isCStyleVarArg() && args.size() < fn.func->getArgumentCount())) + { + error("interp: mismatched argument count in call to '%s': need %zu, received %zu", + fn.func->getName().str(), fn.func->getArgumentCount(), args.size()); + } + + if(fn.blocks.empty() || fn.func->isCStyleVarArg()) + { + // it's probably an extern! + // use libffi. + return runFunctionWithLibFFI(this, ffn, args); + } + else + { + auto entry = prepareFunctionToRun(this, fn, args); + auto ret = runBlock(this, entry); + leaveFunction(this); + + return ret; + } + } + + + + static interp::Value* getVal2(InterpState* is, fir::Value* fv) + { + if(auto it = is->stackFrames.back().values.find(fv); it != is->stackFrames.back().values.end()) + return &it->second; + + else if(auto it2 = is->globals.find(fv); it2 != is->globals.end()) + return &it2->second.first; + + else + return 0; + } + + static interp::Value getVal(InterpState* is, fir::Value* fv) + { + if(auto hmm = getVal2(is, fv); hmm) + return *hmm; + + else if(auto cnst = dcast(fir::ConstantValue, fv); cnst) + return makeConstant(is, cnst); + + else + error("interp: no value with id %zu", fv->id); + } + + + static interp::Value performGEP2(InterpState* is, fir::Type* resty, const interp::Value& ptr, const interp::Value& i1, const interp::Value& i2) + { + + iceAssert(i1.type == i2.type); + + // so, ptr should be a pointer to an array. + iceAssert(ptr.type->isPointerType() && ptr.type->getPointerElementType()->isArrayType()); + + auto arrty = ptr.type->getPointerElementType(); + auto elmty = arrty->getArrayElementType(); + + auto ofs = twoArgumentOp(is, resty, i1, i2, [arrty, elmty](auto a, auto b) -> auto { + return (a * getSizeOfType(arrty)) + (b * getSizeOfType(elmty)); + }); + + auto realptr = getActualValue(ptr); + auto ret = oneArgumentOp(is, resty, ofs, [realptr](auto b) -> auto { + // this is not pointer arithmetic!! + return realptr + b; + }); + + ret.globalValTracker = ptr.globalValTracker; + return ret; + } + + + static interp::Value performStructGEP(InterpState* is, fir::Type* resty, const interp::Value& str, uint64_t idx) + { + iceAssert(str.type->isPointerType()); + auto strty = str.type->getPointerElementType(); + + if(!strty->isStructType() && !strty->isClassType() && !strty->isTupleType()) + error("interp: unsupported type '%s' for struct gep", strty); + + std::vector elms = getTypeListOfType(strty); + + size_t ofs = 0; + for(uint64_t i = 0; i < idx; i++) + ofs += getSizeOfType(elms[i]); + + uintptr_t src = getActualValue(str); + src += ofs; + + auto ret = cloneValue(str); + ret.type = resty; + setValueRaw(is, &ret, &src, sizeof(src)); + + ret.globalValTracker = str.globalValTracker; + return ret; + } + + static interp::Value decay(const interp::Value& val) + { + if(val.val && val.val->islorclvalue()) + { + auto ret = loadFromPtr(val, val.val->getType()); + ret.val = val.val; + + return ret; + } + else + { + return val; + } + } + + static interp::Value& getUndecayedArg(InterpState* is, const interp::Instruction& inst, size_t i) + { + iceAssert(i < inst.args.size()); + auto ret = getVal2(is, inst.args[i]); + iceAssert(ret); + + return *ret; + } + + static interp::Value getArg(InterpState* is, const interp::Instruction& inst, size_t i) + { + iceAssert(i < inst.args.size()); + return decay(getVal(is, inst.args[i])); + } + + static void setRet(InterpState* is, const interp::Instruction& inst, const interp::Value& val) + { + is->stackFrames.back().values[inst.result] = val; + } + + static bool areTypesSufficientlyEqual(fir::Type* a, fir::Type* b) + { + return a == b || (a->isPointerType() && b->isPointerType() && a->getPointerElementType() == b->getPointerElementType()); + } + + + // returns either FLOW_NORMAL, FLOW_BRANCH or FLOW_RETURN. + static int runInstruction(InterpState* is, const interp::Instruction& inst, InstrResult* instrRes) + { + auto ok = (OpKind) inst.opcode; + switch(ok) + { + case OpKind::Signed_Add: + case OpKind::Unsigned_Add: + case OpKind::Floating_Add: + { + iceAssert(inst.args.size() == 2); + auto a = getArg(is, inst, 0); + auto b = getArg(is, inst, 1); + + iceAssert(areTypesSufficientlyEqual(a.type, b.type)); + setRet(is, inst, twoArgumentOp(is, inst, a, b, [](auto a, auto b) -> decltype(a) { + return a + b; + })); + break; + } + + case OpKind::Signed_Sub: + case OpKind::Unsigned_Sub: + case OpKind::Floating_Sub: + { + iceAssert(inst.args.size() == 2); + auto a = getArg(is, inst, 0); + auto b = getArg(is, inst, 1); + + iceAssert(areTypesSufficientlyEqual(a.type, b.type)); + setRet(is, inst, twoArgumentOp(is, inst, a, b, [](auto a, auto b) -> decltype(a) { + return a - b; + })); + break; + } + + case OpKind::Signed_Mul: + case OpKind::Unsigned_Mul: + case OpKind::Floating_Mul: + { + iceAssert(inst.args.size() == 2); + auto a = getArg(is, inst, 0); + auto b = getArg(is, inst, 1); + + iceAssert(areTypesSufficientlyEqual(a.type, b.type)); + setRet(is, inst, twoArgumentOp(is, inst, a, b, [](auto a, auto b) -> decltype(a) { + return a * b; + })); + break; + } + + case OpKind::Signed_Div: + case OpKind::Unsigned_Div: + case OpKind::Floating_Div: + { + iceAssert(inst.args.size() == 2); + auto a = getArg(is, inst, 0); + auto b = getArg(is, inst, 1); + + iceAssert(areTypesSufficientlyEqual(a.type, b.type)); + setRet(is, inst, twoArgumentOp(is, inst, a, b, [](auto a, auto b) -> decltype(a) { + return a / b; + })); + break; + } + + case OpKind::Signed_Mod: + case OpKind::Unsigned_Mod: + { + iceAssert(inst.args.size() == 2); + auto a = getArg(is, inst, 0); + auto b = getArg(is, inst, 1); + + iceAssert(areTypesSufficientlyEqual(a.type, b.type)); + setRet(is, inst, twoArgumentOpIntOnly(is, inst, a, b, [](auto a, auto b) -> decltype(a) { + return a % b; + })); + break; + } + + case OpKind::Floating_Mod: + { + iceAssert(inst.args.size() == 2); + auto a = getArg(is, inst, 0); + auto b = getArg(is, inst, 1); + + iceAssert(areTypesSufficientlyEqual(a.type, b.type)); + setRet(is, inst, twoArgumentOp(is, inst, a, b, [](auto a, auto b) -> decltype(a) { + return fmod(a, b); + })); + break; + } + + + case OpKind::ICompare_Equal: + case OpKind::FCompare_Equal_ORD: + case OpKind::FCompare_Equal_UNORD: + { + iceAssert(inst.args.size() == 2); + auto a = getArg(is, inst, 0); + auto b = getArg(is, inst, 1); + + iceAssert(areTypesSufficientlyEqual(a.type, b.type)); + setRet(is, inst, twoArgumentOp(is, inst, a, b, [](auto a, auto b) -> bool { + return a == b; + })); + break; + } + + case OpKind::ICompare_NotEqual: + case OpKind::FCompare_NotEqual_ORD: + case OpKind::FCompare_NotEqual_UNORD: + { + iceAssert(inst.args.size() == 2); + auto a = getArg(is, inst, 0); + auto b = getArg(is, inst, 1); + + iceAssert(areTypesSufficientlyEqual(a.type, b.type)); + setRet(is, inst, twoArgumentOp(is, inst, a, b, [](auto a, auto b) -> bool { + return a != b; + })); + break; + } + + case OpKind::ICompare_Greater: + case OpKind::FCompare_Greater_ORD: + case OpKind::FCompare_Greater_UNORD: + { + iceAssert(inst.args.size() == 2); + auto a = getArg(is, inst, 0); + auto b = getArg(is, inst, 1); + + iceAssert(areTypesSufficientlyEqual(a.type, b.type)); + setRet(is, inst, twoArgumentOp(is, inst, a, b, [](auto a, auto b) -> bool { + return a > b; + })); + break; + } + + case OpKind::ICompare_Less: + case OpKind::FCompare_Less_ORD: + case OpKind::FCompare_Less_UNORD: + { + iceAssert(inst.args.size() == 2); + auto a = getArg(is, inst, 0); + auto b = getArg(is, inst, 1); + + iceAssert(areTypesSufficientlyEqual(a.type, b.type)); + setRet(is, inst, twoArgumentOp(is, inst, a, b, [](auto a, auto b) -> bool { + return a < b; + })); + break; + } + + + case OpKind::ICompare_GreaterEqual: + case OpKind::FCompare_GreaterEqual_ORD: + case OpKind::FCompare_GreaterEqual_UNORD: + { + iceAssert(inst.args.size() == 2); + auto a = getArg(is, inst, 0); + auto b = getArg(is, inst, 1); + + iceAssert(areTypesSufficientlyEqual(a.type, b.type)); + setRet(is, inst, twoArgumentOp(is, inst, a, b, [](auto a, auto b) -> bool { + return a >= b; + })); + break; + } + + case OpKind::ICompare_LessEqual: + case OpKind::FCompare_LessEqual_ORD: + case OpKind::FCompare_LessEqual_UNORD: + { + iceAssert(inst.args.size() == 2); + auto a = getArg(is, inst, 0); + auto b = getArg(is, inst, 1); + + iceAssert(areTypesSufficientlyEqual(a.type, b.type)); + setRet(is, inst, twoArgumentOp(is, inst, a, b, [](auto a, auto b) -> bool { + return a <= b; + })); + break; + } + + case OpKind::ICompare_Multi: + case OpKind::FCompare_Multi: + { + iceAssert(inst.args.size() == 2); + auto a = getArg(is, inst, 0); + auto b = getArg(is, inst, 1); + + iceAssert(areTypesSufficientlyEqual(a.type, b.type)); + setRet(is, inst, twoArgumentOp(is, inst, a, b, [](auto a, auto b) -> int { + if(a == b) return 0; + if(a > b) return 1; + else return -1; + })); + break; + } + + case OpKind::Bitwise_Xor: + { + iceAssert(inst.args.size() == 2); + auto a = getArg(is, inst, 0); + auto b = getArg(is, inst, 1); + + iceAssert(areTypesSufficientlyEqual(a.type, b.type)); + setRet(is, inst, twoArgumentOpIntOnly(is, inst, a, b, [](auto a, auto b) -> decltype(a) { + return a ^ b; + })); + break; + } + + case OpKind::Bitwise_Logical_Shr: + case OpKind::Bitwise_Arithmetic_Shr: + { + iceAssert(inst.args.size() == 2); + auto a = getArg(is, inst, 0); + auto b = getArg(is, inst, 1); + + iceAssert(a.type->isIntegerType()); + setRet(is, inst, twoArgumentOpIntOnly(is, inst, a, b, [](auto a, auto b) -> decltype(a) { + return a >> b; + })); + break; + } + + case OpKind::Bitwise_Shl: + { + iceAssert(inst.args.size() == 2); + auto a = getArg(is, inst, 0); + auto b = getArg(is, inst, 1); + + iceAssert(a.type->isIntegerType()); + setRet(is, inst, twoArgumentOpIntOnly(is, inst, a, b, [](auto a, auto b) -> decltype(a) { + return a << b; + })); + break; + } + + case OpKind::Bitwise_And: + { + iceAssert(inst.args.size() == 2); + auto a = getArg(is, inst, 0); + auto b = getArg(is, inst, 1); + + iceAssert(areTypesSufficientlyEqual(a.type, b.type)); + setRet(is, inst, twoArgumentOpIntOnly(is, inst, a, b, [](auto a, auto b) -> decltype(a) { + return a & b; + })); + break; + } + + case OpKind::Bitwise_Or: + { + iceAssert(inst.args.size() == 2); + auto a = getArg(is, inst, 0); + auto b = getArg(is, inst, 1); + + iceAssert(areTypesSufficientlyEqual(a.type, b.type)); + setRet(is, inst, twoArgumentOpIntOnly(is, inst, a, b, [](auto a, auto b) -> decltype(a) { + return a | b; + })); + break; + } + + case OpKind::Signed_Neg: + case OpKind::Floating_Neg: + { + iceAssert(inst.args.size() == 1); + auto a = getArg(is, inst, 0); + + setRet(is, inst, oneArgumentOp(is, inst, a, [](auto a) -> auto { + return -1 * a; + })); + break; + } + + case OpKind::Bitwise_Not: + { + iceAssert(inst.args.size() == 1); + auto a = getArg(is, inst, 0); + + setRet(is, inst, oneArgumentOpIntOnly(is, inst, a, [](auto a) -> auto { + return ~a; + })); + break; + } + + case OpKind::Logical_Not: + { + iceAssert(inst.args.size() == 1); + auto a = getArg(is, inst, 0); + + setRet(is, inst, oneArgumentOpIntOnly(is, inst, a, [](auto a) -> auto { + return !a; + })); + break; + } + + case OpKind::Floating_Truncate: + { + iceAssert(inst.args.size() == 1); + auto a = getArg(is, inst, 0); + auto t = inst.args[1]->getType(); + + interp::Value ret; + if(a.type == Type::getFloat64() && t == Type::getFloat32()) + ret = makeValue(inst.result, (float) getActualValue(a)); + + else if(a.type == Type::getFloat32()) ret = makeValue(inst.result, (float) getActualValue(a)); + else if(a.type == Type::getFloat64()) ret = makeValue(inst.result, (double) getActualValue(a)); + else error("interp: unsupported"); + + setRet(is, inst, ret); + break; + } + + case OpKind::Floating_Extend: + { + iceAssert(inst.args.size() == 1); + auto a = getArg(is, inst, 0); + auto t = inst.args[1]->getType(); + + interp::Value ret; + if(a.type == Type::getFloat32() && t == Type::getFloat64()) + ret = makeValue(inst.result, (double) getActualValue(a)); + + else if(a.type == Type::getFloat32()) ret = makeValue(inst.result, (float) getActualValue(a)); + else if(a.type == Type::getFloat64()) ret = makeValue(inst.result, (double) getActualValue(a)); + else error("interp: unsupported"); + + setRet(is, inst, ret); + break; + } + + + + + case OpKind::Value_WritePtr: + { + iceAssert(inst.args.size() == 2); + auto a = getArg(is, inst, 0); + auto b = getArg(is, inst, 1); + + if(a.type != b.type->getPointerElementType()) + error("interp: cannot write '%s' into '%s'", a.type, b.type); + + auto ptr = (void*) getActualValue(b); + if(a.dataSize > LARGE_DATA_SIZE) + { + // just a memcopy. + memmove(ptr, a.ptr, a.dataSize); + } + else + { + // still a memcopy, but slightly more involved. + memmove(ptr, &a.data[0], a.dataSize); + } + + break; + } + + case OpKind::Value_ReadPtr: + { + iceAssert(inst.args.size() == 1); + auto a = getArg(is, inst, 0); + + auto ty = a.type->getPointerElementType(); + + auto ret = loadFromPtr(a, ty); + ret.val = inst.result; + + setRet(is, inst, ret); + break; + } + + + + case OpKind::Value_CreatePHI: + { + iceAssert(inst.args.size() == 1); + auto phi = dcast(fir::PHINode, inst.result); + iceAssert(phi); + + // make the empty thing first + auto val = is->makeValue(inst.result); + + bool found = false; + for(auto [ blk, v ] : phi->getValues()) + { + if(blk == is->stackFrames.back().previousBlock->blk) + { + setValue(is, &val, decay(getVal(is, v))); + found = true; + break; + } + } + + if(!found) error("interp: predecessor was not listed in the PHI node (id %zu)!", phi->id); + + setRet(is, inst, val); + break; + } + + + + + + case OpKind::Value_Return: + { + if(!inst.args.empty()) + instrRes->returnValue = getArg(is, inst, 0); + + return FLOW_RETURN; + } + + case OpKind::Branch_UnCond: + { + iceAssert(inst.args.size() == 1); + auto blk = inst.args[0]; + + const interp::Block* target = 0; + for(const auto& b : is->stackFrames.back().currentFunction->blocks) + { + if(b.blk == blk) + { + target = &b; + break; + } + } + + if(!target) error("interp: branch to block %zu not in current function", blk->id); + + instrRes->targetBlk = target; + return FLOW_BRANCH; + } + + case OpKind::Branch_Cond: + { + iceAssert(inst.args.size() == 3); + auto cond = getArg(is, inst, 0); + iceAssert(cond.type->isBoolType()); + + const interp::Block* trueblk = 0; + const interp::Block* falseblk = 0; + for(const auto& b : is->stackFrames.back().currentFunction->blocks) + { + if(b.blk == inst.args[1]) + trueblk = &b; + + else if(b.blk == inst.args[2]) + falseblk = &b; + + if(trueblk && falseblk) + break; + } + + if(!trueblk || !falseblk) error("interp: branch to blocks %zu or %zu not in current function", trueblk->blk->id, falseblk->blk->id); + + + if(getActualValue(cond)) + instrRes->targetBlk = trueblk; + + else + instrRes->targetBlk = falseblk; + + return FLOW_BRANCH; + } + + + case OpKind::Value_CallFunction: + { + iceAssert(inst.args.size() >= 1); + auto fn = inst.args[0]; + + interp::Function* target = 0; + + // we probably only compiled the entry function, so if we haven't compiled the target then please do + if(auto it = is->compiledFunctions.find(fn); it != is->compiledFunctions.end()) + { + target = &it->second; + } + else + { + for(auto f : is->module->getAllFunctions()) + { + if(f == fn) + { + target = &is->compileFunction(f); + break; + } + } + + if(!target) error("interp: no function %zu (name '%s')", fn->id, fn->getName().str()); + } + + iceAssert(target); + + std::vector args; + for(size_t i = 1; i < inst.args.size(); i++) + args.push_back(getArg(is, inst, i)); + + instrRes->callTarget = target; + instrRes->callArguments = args; + instrRes->callResultValue = inst.result; + + return FLOW_FNCALL; + } + + + + case OpKind::Value_CallFunctionPointer: + { + iceAssert(inst.args.size() >= 1); + auto fn = getArg(is, inst, 0); + + std::vector args; + for(size_t i = 1; i < inst.args.size(); i++) + args.push_back(getArg(is, inst, i)); + + instrRes->callArguments = args; + instrRes->virtualCallTarget = fn; + instrRes->callResultValue = inst.result; + + return FLOW_DYCALL; + } + + + case OpKind::Value_CallVirtualMethod: + { + // args are: 0. classtype, 1. index, 2. functiontype, 3...N args + auto clsty = inst.args[0]->getType()->toClassType(); + auto fnty = inst.args[2]->getType()->toFunctionType(); + iceAssert(clsty); + + std::vector args; + for(size_t i = 3; i < inst.args.size(); i++) + args.push_back(getArg(is, inst, i)); + + //* this is very hacky! we rely on these things not using ::val, because it's null!! + auto vtable = loadFromPtr(performStructGEP(is, fir::Type::getInt8Ptr(), args[0], 0), fir::Type::getInt8Ptr()); + auto vtablety = fir::ArrayType::get(fir::FunctionType::get({ }, fir::Type::getVoid())->getPointerTo(), clsty->getVirtualMethodCount()); + vtable.type = vtablety->getPointerTo(); + + vtable = performGEP2(is, vtablety->getPointerTo(), vtable, makeConstant(is, fir::ConstantInt::getNative(0)), getArg(is, inst, 1)); + + auto fnptr = loadFromPtr(vtable, fnty->getPointerTo()); + + instrRes->callArguments = args; + instrRes->virtualCallTarget = fnptr; + instrRes->callResultValue = inst.result; + + return FLOW_DYCALL; + } + + + + + + case OpKind::Cast_IntSize: + case OpKind::Integer_ZeroExt: + case OpKind::Integer_Truncate: + { + iceAssert(inst.args.size() == 2); + auto a = getArg(is, inst, 0); + auto b = getArg(is, inst, 1); + + interp::Value ret; + + if(a.type->isBoolType()) + { + // since we don't want to add more cases to the thingy, we do a hack -- + // just get the value as a bool, then cast it to an i64 ourselves, then + // pass *that* to the ops. + + auto boolval = (int64_t) getActualValue(a); + ret = oneArgumentOp(is, inst, b, [boolval](auto b) -> auto { + return (decltype(b)) boolval; + }); + } + else if(b.type->isBoolType()) + { + ret = oneArgumentOp(is, inst, a, [](auto a) -> auto { + return (bool) a; + }); + } + else + { + ret = twoArgumentOp(is, inst, a, b, [](auto a, auto b) -> auto { + return (decltype(b)) a; + }); + } + + setRet(is, inst, ret); + break; + } + + + // TODO: these could be made more robust!! + case OpKind::Cast_Bitcast: + case OpKind::Cast_Signedness: + case OpKind::Cast_IntSignedness: + case OpKind::Cast_PointerType: + case OpKind::Cast_PointerToInt: + case OpKind::Cast_IntToPointer: + { + iceAssert(inst.args.size() == 2); + + auto v = cloneValue(inst.result, getArg(is, inst, 0)); + v.type = inst.args[1]->getType(); + + setRet(is, inst, v); + break; + } + + case OpKind::Cast_FloatToInt: + { + iceAssert(inst.args.size() == 2); + auto a = getArg(is, inst, 0); + auto b = getArg(is, inst, 1); + + interp::Value ret = twoArgumentOp(is, inst, a, b, [](auto a, auto b) -> auto { + return (decltype(b)) a; + }); + + setRet(is, inst, ret); + break; + } + + case OpKind::Cast_IntToFloat: + { + iceAssert(inst.args.size() == 2); + auto a = getArg(is, inst, 0); + auto b = getArg(is, inst, 1); + + interp::Value ret = twoArgumentOp(is, inst, a, b, [](auto a, auto b) -> auto { + return (decltype(b)) a; + }); + + setRet(is, inst, ret); + break; + } + + + case OpKind::Value_GetPointer: + { + // equivalent to GEP(ptr*, index) + iceAssert(inst.args.size() == 2); + auto ptr = getArg(is, inst, 0); + auto b = getArg(is, inst, 1); + + iceAssert(ptr.type->isPointerType()); + iceAssert(b.type->isIntegerType()); + + auto elmty = ptr.type->getPointerElementType(); + + auto realptr = getActualValue(ptr); + setRet(is, inst, oneArgumentOp(is, inst, b, [realptr, elmty](auto b) -> auto { + // this doesn't do pointer arithmetic!! if it's a pointer type, the value we get + // will be a uintptr_t. + return realptr + (b * getSizeOfType(elmty)); + })); + + break; + } + + case OpKind::Value_GetGEP2: + { + // equivalent to GEP(ptr*, index1, index2) + iceAssert(inst.args.size() == 3); + auto ptr = getArg(is, inst, 0); + auto i1 = getArg(is, inst, 1); + auto i2 = getArg(is, inst, 2); + + auto ret = performGEP2(is, inst.result->getType(), ptr, i1, i2); + ret.val = inst.result; + + setRet(is, inst, ret); + break; + } + + case OpKind::Value_GetStructMember: + { + // equivalent to GEP(ptr*, 0, memberIndex) + iceAssert(inst.args.size() == 2); + auto str = getUndecayedArg(is, inst, 0); + auto idx = dcast(fir::ConstantInt, inst.args[1])->getUnsignedValue(); + + auto ret = performStructGEP(is, inst.result->getType()->getPointerTo(), str, idx); + ret.val = inst.result; + + setRet(is, inst, ret); + break; + } + + + + case OpKind::Value_GetPointerToStructMember: + { + // equivalent to llvm's GEP(ptr*, ptrIndex, memberIndex) + error("interp: enotsup"); + } + + + + + + case OpKind::Misc_Sizeof: + { + iceAssert(inst.args.size() == 1); + auto ty = inst.args[0]->getType(); + + auto ci = fir::ConstantInt::getNative(getSizeOfType(ty)); + + if(fir::getNativeWordSizeInBits() == 64) setRet(is, inst, makeValue(inst.result, (int64_t) ci->getSignedValue())); + if(fir::getNativeWordSizeInBits() == 32) setRet(is, inst, makeValue(inst.result, (int32_t) ci->getSignedValue())); + if(fir::getNativeWordSizeInBits() == 16) setRet(is, inst, makeValue(inst.result, (int16_t) ci->getSignedValue())); + if(fir::getNativeWordSizeInBits() == 8) setRet(is, inst, makeValue(inst.result, (int8_t) ci->getSignedValue())); + + break; + } + + + case OpKind::Value_CreateLVal: + case OpKind::Value_StackAlloc: + { + iceAssert(inst.args.size() == 1); + + auto ty = inst.args[0]->getType(); + auto sz = getSizeOfType(ty); + + void* buffer = new uint8_t[sz]; + memset(buffer, 0, sz); + + is->stackFrames.back().stackAllocs.push_back(buffer); + + auto ret = makeValueOfType(inst.result, ty->getPointerTo()); + setValueRaw(is, &ret, &buffer, sizeof(void*)); + + setRet(is, inst, ret); + break; + } + + case OpKind::Value_Store: + { + iceAssert(inst.args.size() == 2); + auto a = getArg(is, inst, 0); + auto b = getUndecayedArg(is, inst, 1); + + iceAssert(inst.args[1]->islorclvalue()); + + auto ptr = (void*) getActualValue(b); + if(a.dataSize > LARGE_DATA_SIZE) memmove(ptr, a.ptr, a.dataSize); + else memmove(ptr, &a.data[0], a.dataSize); + + if(b.globalValTracker) + { + // set the modified flag to true! + is->globals[b.globalValTracker].second = true; + } + break; + } + + case OpKind::Value_AddressOf: + { + iceAssert(inst.args.size() == 1); + auto ret = cloneValue(inst.result, getUndecayedArg(is, inst, 0)); + + setRet(is, inst, ret); + break; + } + + case OpKind::Value_Dereference: + { + iceAssert(inst.args.size() == 1); + auto a = getArg(is, inst, 0); + auto ret = cloneValue(inst.result, a); + + setRet(is, inst, ret); + break; + } + + case OpKind::Value_Select: + { + iceAssert(inst.args.size() == 3); + auto cond = getArg(is, inst, 0); + iceAssert(cond.type->isBoolType()); + + auto trueval = getArg(is, inst, 1); + auto falseval = getArg(is, inst, 2); + + if(getActualValue(cond)) setRet(is, inst, trueval); + else setRet(is, inst, falseval); + + break; + } + + + + case OpKind::Value_InsertValue: + { + iceAssert(inst.args.size() == 3); + + auto str = getArg(is, inst, 0); + auto elm = getArg(is, inst, 1); + auto idx = (size_t) getActualValue(getArg(is, inst, 2)); + + setRet(is, inst, doInsertValue(is, inst.result, str, elm, idx)); + break; + } + + + case OpKind::Value_ExtractValue: + { + iceAssert(inst.args.size() >= 2); + + auto str = getArg(is, inst, 0); + auto idx = (size_t) getActualValue(getArg(is, inst, 1)); + + setRet(is, inst, doExtractValue(is, inst.result, str, idx)); + break; + } + + + case OpKind::SAA_GetData: + case OpKind::SAA_GetLength: + case OpKind::SAA_GetCapacity: + case OpKind::SAA_GetRefCountPtr: + { + iceAssert(inst.args.size() == 1); + auto str = getArg(is, inst, 0); + + interp::Value ret; + + if(ok == OpKind::SAA_GetData) + ret = doExtractValue(is, inst.result, str, SAA_DATA_INDEX); + + else if(ok == OpKind::SAA_GetLength) + ret = doExtractValue(is, inst.result, str, SAA_LENGTH_INDEX); + + else if(ok == OpKind::SAA_GetCapacity) + ret = doExtractValue(is, inst.result, str, SAA_CAPACITY_INDEX); + + else if(ok == OpKind::SAA_GetRefCountPtr) + ret = doExtractValue(is, inst.result, str, SAA_REFCOUNTPTR_INDEX); + + setRet(is, inst, ret); + break; + } + + case OpKind::SAA_SetData: + case OpKind::SAA_SetLength: + case OpKind::SAA_SetCapacity: + case OpKind::SAA_SetRefCountPtr: + { + iceAssert(inst.args.size() == 2); + auto str = getArg(is, inst, 0); + auto elm = getArg(is, inst, 1); + + interp::Value ret; + + if(ok == OpKind::SAA_SetData) + ret = doInsertValue(is, inst.result, str, elm, SAA_DATA_INDEX); + + else if(ok == OpKind::SAA_SetLength) + ret = doInsertValue(is, inst.result, str, elm, SAA_LENGTH_INDEX); + + else if(ok == OpKind::SAA_SetCapacity) + ret = doInsertValue(is, inst.result, str, elm, SAA_CAPACITY_INDEX); + + else if(ok == OpKind::SAA_SetRefCountPtr) + ret = doInsertValue(is, inst.result, str, elm, SAA_REFCOUNTPTR_INDEX); + + setRet(is, inst, ret); + break; + } + + case OpKind::ArraySlice_GetData: + case OpKind::ArraySlice_GetLength: + { + iceAssert(inst.args.size() == 1); + auto str = getArg(is, inst, 0); + + interp::Value ret; + + if(ok == OpKind::ArraySlice_GetData) + ret = doExtractValue(is, inst.result, str, SLICE_DATA_INDEX); + + else if(ok == OpKind::ArraySlice_GetLength) + ret = doExtractValue(is, inst.result, str, SLICE_LENGTH_INDEX); + + setRet(is, inst, ret); + break; + } + + + + case OpKind::ArraySlice_SetData: + case OpKind::ArraySlice_SetLength: + { + iceAssert(inst.args.size() == 2); + auto str = getArg(is, inst, 0); + auto elm = getArg(is, inst, 1); + + interp::Value ret; + + if(ok == OpKind::ArraySlice_SetData) + ret = doInsertValue(is, inst.result, str, elm, SLICE_DATA_INDEX); + + else if(ok == OpKind::ArraySlice_SetLength) + ret = doInsertValue(is, inst.result, str, elm, SLICE_LENGTH_INDEX); + + setRet(is, inst, ret); + break; + } + + case OpKind::Any_GetData: + case OpKind::Any_GetTypeID: + case OpKind::Any_GetRefCountPtr: + { + iceAssert(inst.args.size() == 1); + auto str = getArg(is, inst, 0); + + interp::Value ret; + + if(ok == OpKind::Any_GetTypeID) + ret = doExtractValue(is, inst.result, str, ANY_TYPEID_INDEX); + + else if(ok == OpKind::Any_GetRefCountPtr) + ret = doExtractValue(is, inst.result, str, ANY_REFCOUNTPTR_INDEX); + + else if(ok == OpKind::Any_GetData) + ret = doExtractValue(is, inst.result, str, ANY_DATA_ARRAY_INDEX); + + setRet(is, inst, ret); + break; + } + + case OpKind::Any_SetData: + case OpKind::Any_SetTypeID: + case OpKind::Any_SetRefCountPtr: + { + iceAssert(inst.args.size() == 2); + auto str = getArg(is, inst, 0); + auto elm = getArg(is, inst, 1); + + interp::Value ret; + + if(ok == OpKind::Any_SetTypeID) + ret = doInsertValue(is, inst.result, str, elm, ANY_TYPEID_INDEX); + + else if(ok == OpKind::Any_SetRefCountPtr) + ret = doInsertValue(is, inst.result, str, elm, ANY_REFCOUNTPTR_INDEX); + + else if(ok == OpKind::Any_SetData) + ret = doInsertValue(is, inst.result, str, elm, ANY_DATA_ARRAY_INDEX); + + setRet(is, inst, ret); + break; + } + + + + case OpKind::Range_GetLower: + case OpKind::Range_GetUpper: + case OpKind::Range_GetStep: + { + iceAssert(inst.args.size() == 1); + auto str = getArg(is, inst, 0); + + interp::Value ret; + + if(ok == OpKind::Range_GetLower) + ret = doExtractValue(is, inst.result, str, 0); + + else if(ok == OpKind::Range_GetUpper) + ret = doExtractValue(is, inst.result, str, 1); + + else if(ok == OpKind::Range_GetStep) + ret = doExtractValue(is, inst.result, str, 2); + + setRet(is, inst, ret); + break; + } + + + case OpKind::Range_SetLower: + case OpKind::Range_SetUpper: + case OpKind::Range_SetStep: + { + iceAssert(inst.args.size() == 2); + auto str = getArg(is, inst, 0); + auto elm = getArg(is, inst, 1); + + interp::Value ret; + + if(ok == OpKind::Range_SetLower) + ret = doInsertValue(is, inst.result, str, elm, 0); + + else if(ok == OpKind::Range_SetUpper) + ret = doInsertValue(is, inst.result, str, elm, 1); + + else if(ok == OpKind::Range_SetStep) + ret = doInsertValue(is, inst.result, str, elm, 2); + + setRet(is, inst, ret); + break; + } + + + + case OpKind::Enum_GetIndex: + case OpKind::Enum_GetValue: + { + iceAssert(inst.args.size() == 1); + auto str = getArg(is, inst, 0); + + interp::Value ret; + + if(ok == OpKind::Enum_GetIndex) + ret = doExtractValue(is, inst.result, str, 0); + + else if(ok == OpKind::Enum_GetValue) + ret = doExtractValue(is, inst.result, str, 1); + + setRet(is, inst, ret); + break; + } + + + case OpKind::Enum_SetIndex: + case OpKind::Enum_SetValue: + { + iceAssert(inst.args.size() == 2); + auto str = getArg(is, inst, 0); + auto elm = getArg(is, inst, 1); + + interp::Value ret; + + if(ok == OpKind::Enum_SetIndex) + ret = doInsertValue(is, inst.result, str, elm, 0); + + else if(ok == OpKind::Enum_SetValue) + ret = doInsertValue(is, inst.result, str, elm, 1); + + setRet(is, inst, ret); + break; + } + + + case OpKind::Union_GetVariantID: + { + iceAssert(inst.args.size() == 1); + auto str = getArg(is, inst, 0); + + setRet(is, inst, doExtractValue(is, inst.result, str, 0)); + break; + } + + case OpKind::Union_SetVariantID: + { + iceAssert(inst.args.size() == 2); + auto str = getArg(is, inst, 0); + auto elm = getArg(is, inst, 1); + + setRet(is, inst, doInsertValue(is, inst.result, str, elm, 0)); + break; + } + + case OpKind::Union_GetValue: + { + iceAssert(inst.args.size() == 2); + iceAssert(inst.args[0]->getType()->isUnionType()); + + auto ut = inst.args[0]->getType()->toUnionType(); + auto vid = dcast(fir::ConstantInt, inst.args[1])->getSignedValue(); + + iceAssert((size_t) vid < ut->getVariantCount()); + auto vt = ut->getVariant(vid)->getInteriorType(); + + // because we can operate with the raw memory values, we can probably do this a bit more efficiently + // than we can with LLVM, where we needed to create a temporary stack value to store the thing from + // the extractvalue so we could cast-to-pointer then load. + + // first we just get the argument: + auto theUnion = getArg(is, inst, 0); + + // then, get the array: + uintptr_t arrayAddr = 0; + if(theUnion.dataSize > LARGE_DATA_SIZE) arrayAddr = (uintptr_t) theUnion.ptr; + else arrayAddr = (uintptr_t) &theUnion.data[0]; + + // offset it appropriately: + arrayAddr += getSizeOfType(fir::Type::getNativeWord()); + + // ok so now we just do a 'setRaw' to get the value out. + auto ret = is->makeValue(inst.result); + setValueRaw(is, &ret, (void*) arrayAddr, getSizeOfType(vt)); + + setRet(is, inst, ret); + break; + } + + + case OpKind::Union_SetValue: + { + iceAssert(inst.args.size() == 3); + iceAssert(inst.args[0]->getType()->isUnionType()); + + auto ut = inst.args[0]->getType()->toUnionType(); + auto vid = (intptr_t) dcast(fir::ConstantInt, inst.args[1])->getSignedValue(); + + iceAssert((size_t) vid < ut->getVariantCount()); + + // again, we do this "manually" because we can access the raw bytes, so we don't have to + // twist ourselves through hoops like with llvm. + + // first we just get the argument: + auto theUnion = cloneValue(inst.result, getArg(is, inst, 0)); + + // then, get the array: + uintptr_t baseAddr = 0; + if(theUnion.dataSize > LARGE_DATA_SIZE) baseAddr = (uintptr_t) theUnion.ptr; + else baseAddr = (uintptr_t) &theUnion.data[0]; + + // offset it appropriately: + auto arrayAddr = baseAddr + getSizeOfType(fir::Type::getNativeWord()); + + // ok, now we just do a memcpy into the struct. + iceAssert(sizeof(intptr_t) == (fir::getNativeWordSizeInBits() / CHAR_BIT)); + memmove((void*) baseAddr, &vid, sizeof(intptr_t)); + + uintptr_t valueAddr = 0; + auto theValue = getArg(is, inst, 2); + if(theValue.dataSize > LARGE_DATA_SIZE) valueAddr = (uintptr_t) theValue.ptr; + else valueAddr = (uintptr_t) &theValue.data[0]; + + memmove((void*) arrayAddr, (void*) valueAddr, theValue.dataSize); + + setRet(is, inst, theUnion); + break; + } + + + case OpKind::RawUnion_GEP: + { + iceAssert(inst.args.size() == 2); + auto targtype = inst.args[1]->getType(); + + iceAssert(inst.args[0]->islorclvalue()); + + // again. just manipulate the memory. + auto unn = getUndecayedArg(is, inst, 0); + auto buffer = getActualValue(unn); + + auto ret = makeValueOfType(inst.result, targtype->getPointerTo()); + setValueRaw(is, &ret, &buffer, sizeof(uintptr_t)); + + setRet(is, inst, ret); + break; + } + + + + case OpKind::Unreachable: + { + error("interp: unreachable op!"); + } + + case OpKind::Invalid: + default: + { + // note we don't use "default" to catch + // new opkinds that we forget to add. + error("interp: invalid opcode %d!", inst.opcode); + } + } + + return FLOW_NORMAL; + } + + + fir::ConstantValue* InterpState::unwrapInterpValueIntoConstant(const interp::Value& val) + { + auto ty = val.type; + + #define gav getActualValue + + if(ty->isPrimitiveType() || ty->isBoolType()) + { + if(ty == fir::Type::getBool()) return fir::ConstantBool::get(gav(val)); + else if(ty == fir::Type::getInt8()) return fir::ConstantInt::get(ty, gav(val)); + else if(ty == fir::Type::getInt16()) return fir::ConstantInt::get(ty, gav(val)); + else if(ty == fir::Type::getInt32()) return fir::ConstantInt::get(ty, gav(val)); + else if(ty == fir::Type::getInt64()) return fir::ConstantInt::get(ty, gav(val)); + else if(ty == fir::Type::getUint8()) return fir::ConstantInt::get(ty, gav(val)); + else if(ty == fir::Type::getUint16()) return fir::ConstantInt::get(ty, gav(val)); + else if(ty == fir::Type::getUint32()) return fir::ConstantInt::get(ty, gav(val)); + else if(ty == fir::Type::getUint64()) return fir::ConstantInt::get(ty, gav(val)); + else if(ty == fir::Type::getFloat32()) return fir::ConstantFP::get(ty, gav(val)); + else if(ty == fir::Type::getFloat64()) return fir::ConstantFP::get(ty, gav(val)); + } + + #undef gav + + error("interp: cannot unwrap type '%s'", ty); + } +} +} + + +#ifdef _MSC_VER + #pragma warning(pop) +#else + #pragma GCC diagnostic pop +#endif + + + + + + + + + + + + + + + + diff --git a/source/fir/interp/wrappers.cpp b/source/fir/interp/wrappers.cpp new file mode 100644 index 00000000..2d65a811 --- /dev/null +++ b/source/fir/interp/wrappers.cpp @@ -0,0 +1,102 @@ +// wrappers.cpp +// Copyright (c) 2019, zhiayang +// Licensed under the Apache License Version 2.0. + +#include "defs.h" +#include "platform.h" +#include "ir/module.h" + +#include "ir/interp.h" + + +PLATFORM_EXPORT_FUNCTION +void __interp_intrinsic_memset(void* dst, int8_t val, size_t cnt, bool) +{ + memset(dst, val, cnt); +} + +PLATFORM_EXPORT_FUNCTION +void __interp_intrinsic_memcpy(void* dst, void* src, size_t cnt, bool) +{ + memcpy(dst, src, cnt); +} + +PLATFORM_EXPORT_FUNCTION +void __interp_intrinsic_memmove(void* dst, void* src, size_t cnt, bool) +{ + memmove(dst, src, cnt); +} + +PLATFORM_EXPORT_FUNCTION +int __interp_intrinsic_memcmp(void* a, void*b, size_t cnt, bool) +{ + return memcmp(a, b, cnt); +} + +PLATFORM_EXPORT_FUNCTION +int64_t __interp_intrinsic_roundup_pow2(int64_t x) +{ + auto num = x; + auto ret = 1; + + while(num > 0) + { + num >>= 1; + ret <<= 1; + } + + return ret; +} + + + + + + + +PLATFORM_EXPORT_FUNCTION int __interp_wrapper_printf(char* fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + + int ret = vprintf(fmt, ap); + + va_end(ap); + return ret; +} + +PLATFORM_EXPORT_FUNCTION int __interp_wrapper_sprintf(char* buf, char* fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + + int ret = vsprintf(buf, fmt, ap); + + va_end(ap); + return ret; +} + +PLATFORM_EXPORT_FUNCTION int __interp_wrapper_snprintf(char* buf, size_t n, char* fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + + int ret = vsnprintf(buf, n, fmt, ap); + + va_end(ap); + return ret; +} + +PLATFORM_EXPORT_FUNCTION int __interp_wrapper_fprintf(void* stream, char* fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + + int ret = vfprintf((FILE*) stream, fmt, ap); + + va_end(ap); + return ret; +} + + + diff --git a/source/frontend/collector.cpp b/source/frontend/collector.cpp index 49b4fc19..0b742d1a 100644 --- a/source/frontend/collector.cpp +++ b/source/frontend/collector.cpp @@ -1,5 +1,5 @@ // collector.cpp -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #include @@ -80,6 +80,10 @@ namespace frontend sst::DefinitionTree* typecheckFiles(CollectorState* state) { + // do a simple thing. + if(state->nativeWordSize != 0) fir::setNativeWordSizeInBits(state->nativeWordSize); + else fir::setNativeWordSizeInBits(64); + // typecheck for(const auto& file : state->allFiles) { @@ -104,11 +108,10 @@ namespace frontend } imports.push_back({ ithing, stree }); - - // debuglog("%s depends on %s\n", frontend::getFilenameFromPath(file).c_str(), frontend::getFilenameFromPath(d->name).c_str()); } - state->dtrees[file] = sst::typecheck(state, state->parsed[file], imports); + // i guess we always add prelude definitions? + state->dtrees[file] = sst::typecheck(state, state->parsed[file], imports, /* addPreludeDefinitions: */ true); } return state->dtrees[state->fullMainFile]; diff --git a/source/frontend/dependencies.cpp b/source/frontend/dependencies.cpp index 38f3c805..914bda94 100644 --- a/source/frontend/dependencies.cpp +++ b/source/frontend/dependencies.cpp @@ -1,5 +1,5 @@ // dependencies.cpp -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "ast.h" diff --git a/source/frontend/errors.cpp b/source/frontend/errors.cpp index d68a7995..4bfb7296 100644 --- a/source/frontend/errors.cpp +++ b/source/frontend/errors.cpp @@ -1,5 +1,5 @@ // errors.cpp -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "defs.h" @@ -87,11 +87,12 @@ static std::string fetchContextLine(Location loc, size_t* adjust) -// #define UNDERLINE_CHARACTER " ̅" -#define UNDERLINE_CHARACTER "^" +// #define UNDERLINE_CHARACTER "^" +#define UNDERLINE_CHARACTER "\u203e" -std::string getSpannedContext(const Location& loc, const std::vector& spans, size_t* adjust, size_t* num_width, size_t* margin, - bool underline, bool bottompad, std::string underlineColour) + +static std::string getSpannedContext(const Location& loc, const std::vector& spans, size_t* adjust, size_t* num_width, + size_t* margin, bool underline, bool bottompad, const std::string& underlineColour, const std::string& overrideLineContent) { std::string ret; @@ -99,14 +100,16 @@ std::string getSpannedContext(const Location& loc, const std::vector bool { return a.loc.col < b.loc.col; })) - _error_and_exit("spans must be sorted!"); + _error_and_exit("spans must be sorted!\n"); *num_width = std::to_string(loc.line + 1).length(); // one spacing line *margin = *num_width + 2; ret += strprintf("%s |\n", spaces(*num_width)); - ret += strprintf("%d |%s\n", loc.line + 1, fetchContextLine(loc, adjust)); + + if(overrideLineContent.empty()) ret += strprintf("%d |%s\n", loc.line + 1, fetchContextLine(loc, adjust)); + else ret += strprintf("%s |%s%s\n", spaces(*num_width), spaces(LEFT_PADDING), overrideLineContent); if(bottompad) ret += strprintf("%s |", spaces(*num_width)); @@ -122,11 +125,13 @@ std::string getSpannedContext(const Location& loc, const std::vector 0) { auto location = strprintf("%s:%d:%d", filename, loc.line + 1, loc.col + 1); - ret += strprintf("%s%sat:%s %s%s", COLOUR_RESET, COLOUR_GREY_BOLD, spaces(strlen(type) - 2), COLOUR_RESET, location); + ret += strprintf("%s%sat:%s %s%s\n", COLOUR_RESET, COLOUR_GREY_BOLD, spaces(strlen(type) - 2), COLOUR_RESET, location); } - ret += "\n"; if(context && loc.fileID > 0) - ret += getSingleContext(loc) + "\n"; + { + auto underlineColour = COLOUR_RED_BOLD; + + if(strcmp(type, "note") == 0) + underlineColour = COLOUR_BLUE_BOLD; + + ret += getSingleContext(loc, underlineColour) + "\n"; + } return ret; } @@ -238,7 +250,7 @@ void SimpleError::post() { outputWithoutContext(typestr(this->type).c_str(), this->loc, this->msg.c_str()); strprinterrf("%s%s%s", this->wordsBeforeContext, this->wordsBeforeContext.size() > 0 ? "\n" : "", - this->printContext ? getSingleContext(this->loc) + "\n\n" : ""); + this->printContext ? getSingleContext(this->loc, this->type == MsgType::Note ? COLOUR_BLUE_BOLD : COLOUR_RED_BOLD) + "\n\n" : ""); } for(auto other : this->subs) @@ -246,6 +258,16 @@ void SimpleError::post() } +void ExampleMsg::post() +{ + outputWithoutContext(typestr(this->type).c_str(), Location(), "for example:"); + strprinterrf("%s\n\n", getSingleContext(Location(), COLOUR_BLUE_BOLD, this->example)); + + for(auto other : this->subs) + other->post(); +} + + namespace util { static bool operator == (const util::ESpan& a, const util::ESpan& b) { return a.loc == b.loc && a.msg == b.msg; } @@ -270,6 +292,11 @@ namespace util { return util::pool(se, t); } + + ExampleMsg* make_ExampleMsg(const std::string& eg, MsgType t) + { + return util::pool(eg, t); + } } @@ -305,7 +332,7 @@ void SpanError::post() std::sort(this->spans.begin(), this->spans.end(), [](auto a, auto b) -> bool { return a.loc.col < b.loc.col; }); this->spans.erase(std::unique(this->spans.begin(), this->spans.end()), this->spans.end()); - strprinterrf("%s\n", getSpannedContext(this->top->loc, this->spans, &adjust, &num_width, &margin, true, true, COLOUR_CYAN_BOLD)); + strprinterrf("%s\n", getSpannedContext(this->top->loc, this->spans, &adjust, &num_width, &margin, true, true, COLOUR_CYAN_BOLD, "")); // ok now remove the extra thing. if(didExtra) @@ -417,6 +444,7 @@ void OverloadError::post() { if(emg->kind != ErrKind::Span) { + emg->type = MsgType::Note; emg->post(); } else diff --git a/source/frontend/file.cpp b/source/frontend/file.cpp index 975901fb..388ab41e 100644 --- a/source/frontend/file.cpp +++ b/source/frontend/file.cpp @@ -1,5 +1,5 @@ // file.cpp -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #include diff --git a/source/frontend/frontend.cpp b/source/frontend/frontend.cpp index 98ef932b..a0d281a2 100644 --- a/source/frontend/frontend.cpp +++ b/source/frontend/frontend.cpp @@ -1,5 +1,5 @@ // frontend.cpp -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "defs.h" @@ -8,82 +8,86 @@ #include - -#define VERSION_STRING "0.30.0-pre" - -#define ARG_COMPILE_ONLY "-c" -#define ARG_BACKEND "-backend" -#define ARG_EMIT_LLVM_IR "-emit-llvm" -#define ARG_LINK_FRAMEWORK "-framework" -#define ARG_FRAMEWORK_SEARCH_PATH "-F" -#define ARG_HELP "-help" -#define ARG_VERSION "-version" -#define ARG_JITPROGRAM "-jit" -#define ARG_LINK_LIBRARY "-l" -#define ARG_LIBRARY_SEARCH_PATH "-L" -#define ARG_MCMODEL "-mcmodel" -#define ARG_DISABLE_AUTO_GLOBAL_CONSTRUCTORS "-no-auto-gconstr" -#define ARG_OUTPUT_FILE "-o" -#define ARG_OPTIMISATION_LEVEL_SELECT "-O" -#define ARG_POSINDEPENDENT "-pic" -#define ARG_PRINT_FIR "-print-fir" -#define ARG_PRINT_LLVMIR "-print-lir" -#define ARG_PROFILE "-profile" -#define ARG_RUNPROGRAM "-run" -#define ARG_SHOW_CLANG_OUTPUT "-show-clang" -#define ARG_SYSROOT "-sysroot" -#define ARG_TARGET "-target" -#define ARG_FREESTANDING "-ffreestanding" +#define FLAX_VERSION_STRING "0.40.0-pre" + +#define ARG_COMPILE_ONLY "-c" +#define ARG_BACKEND "-backend" +#define ARG_EMIT_LLVM_IR "-emit-llvm" +#define ARG_LINK_FRAMEWORK "-framework" +#define ARG_FRAMEWORK_SEARCH_PATH "-F" +#define ARG_HELP "-help" +#define ARG_VERSION "-version" +#define ARG_JITPROGRAM "-jit" +#define ARG_LINK_LIBRARY "-l" +#define ARG_LIBRARY_SEARCH_PATH "-L" +#define ARG_MCMODEL "-mcmodel" +#define ARG_DISABLE_AUTO_GLOBAL_CONSTRUCTORS "-no-auto-gconstr" +#define ARG_OUTPUT_FILE "-o" +#define ARG_OPTIMISATION_LEVEL_SELECT "-O" +#define ARG_POSINDEPENDENT "-pic" +#define ARG_PRINT_FIR "-print-fir" +#define ARG_PRINT_LLVMIR "-print-lir" +#define ARG_PROFILE "-profile" +#define ARG_RUNPROGRAM "-run" +#define ARG_SHOW_CLANG_OUTPUT "-show-clang" +#define ARG_SYSROOT "-sysroot" +#define ARG_TARGET "-target" +#define ARG_FREESTANDING "-ffreestanding" +#define ARG_NO_RUNTIME_CHECKS "-no-runtime-checks" +#define ARG_NO_RUNTIME_ERROR_STRINGS "-no-runtime-error-strings" // for internal use! #define ARG_ABORT_ON_ERROR "-abort-on-error" -#define WARNING_DISABLE_ALL "-w" -#define WARNINGS_AS_ERRORS "-Werror" +#define WARNING_DISABLE_ALL "-w" +#define WARNINGS_AS_ERRORS "-Werror" // actual warnings -#define WARNING_ENABLE_UNUSED_VARIABLE "-Wunused-variable" -#define WARNING_ENABLE_VARIABLE_CHECKER "-Wvar-checker" +#define WARNING_ENABLE_UNUSED_VARIABLE "-Wunused-variable" +#define WARNING_ENABLE_VARIABLE_CHECKER "-Wvar-checker" -#define WARNING_DISABLE_UNUSED_VARIABLE "-Wno-unused-variable" -#define WARNING_DISABLE_VARIABLE_CHECKER "-Wno-var-checker" +#define WARNING_DISABLE_UNUSED_VARIABLE "-Wno-unused-variable" +#define WARNING_DISABLE_VARIABLE_CHECKER "-Wno-var-checker" static std::vector> list; static void setupMap() { - list.push_back({ ARG_COMPILE_ONLY, "Output an object file; do not call the linker" }); - list.push_back({ ARG_BACKEND + std::string(" "), "Change the backend used for compilation" }); - list.push_back({ ARG_EMIT_LLVM_IR, "Emit a bitcode (.bc) file instead of a program" }); - list.push_back({ ARG_LINK_FRAMEWORK + std::string(" "), "Link to a framework (macOS only)" }); - list.push_back({ ARG_LINK_FRAMEWORK + std::string(" "), "Link to a framework (macOS only)" }); - list.push_back({ ARG_HELP, "Print this message" }); - list.push_back({ ARG_JITPROGRAM, "Use LLVM JIT to run the program, instead of compiling to a file" }); - list.push_back({ ARG_LINK_LIBRARY + std::string(" "), "Link to a library" }); - list.push_back({ ARG_LIBRARY_SEARCH_PATH + std::string(" "), "Search for libraries in " }); - list.push_back({ ARG_MCMODEL + std::string(" "), "Change the mcmodel of the code" }); - list.push_back({ ARG_DISABLE_AUTO_GLOBAL_CONSTRUCTORS, "Disable calling constructors before main()" }); - list.push_back({ ARG_OUTPUT_FILE + std::string(" "), "Set the name of the output file" }); - list.push_back({ ARG_OPTIMISATION_LEVEL_SELECT + std::string(""), "Change the optimisation level; -O0, -O1, -O2, -O3, and -Ox are valid options" }); - - list.push_back({ ARG_POSINDEPENDENT, "Generate position independent code" }); - list.push_back({ ARG_PRINT_FIR, "Print the FlaxIR before compilation" }); - list.push_back({ ARG_PRINT_LLVMIR, "Print the LLVM IR before compilation" }); - list.push_back({ ARG_PROFILE, "Print internal compiler profiling statistics" }); - list.push_back({ ARG_RUNPROGRAM, "Use LLVM JIT to run the program, instead of compiling to a file" }); - list.push_back({ ARG_SHOW_CLANG_OUTPUT, "Show the output of calling the final compiler" }); - list.push_back({ ARG_SYSROOT + std::string(" "), "Set the directory used as the sysroot" }); - list.push_back({ ARG_TARGET + std::string(" "), "Change the compilation target" }); - - list.push_back({ WARNING_DISABLE_ALL, "Disable all warnings" }); - list.push_back({ WARNINGS_AS_ERRORS, "Treat all warnings as errors" }); - - - list.push_back({ WARNING_ENABLE_UNUSED_VARIABLE, "Enable warnings for unused variables" }); - list.push_back({ WARNING_ENABLE_VARIABLE_CHECKER, "Enable warnings from the variable state checker" }); - list.push_back({ WARNING_DISABLE_UNUSED_VARIABLE, "Disable warnings for unused variables" }); - list.push_back({ WARNING_DISABLE_VARIABLE_CHECKER, "Disable warnings from the variable state checker" }); + list.push_back({ ARG_COMPILE_ONLY, "output an object file; do not call the linker" }); + list.push_back({ ARG_BACKEND + std::string(" "), "change the backend used for compilation" }); + list.push_back({ ARG_EMIT_LLVM_IR, "emit a bitcode (.bc) file instead of a program" }); + list.push_back({ ARG_LINK_FRAMEWORK + std::string(" "), "link to a framework (macOS only)" }); + list.push_back({ ARG_LINK_FRAMEWORK + std::string(" "), "link to a framework (macOS only)" }); + list.push_back({ ARG_HELP, "print this message" }); + list.push_back({ ARG_JITPROGRAM, "use LLVM JIT to run the program, instead of compiling to a file" }); + list.push_back({ ARG_LINK_LIBRARY + std::string(" "), "link to a library" }); + list.push_back({ ARG_LIBRARY_SEARCH_PATH + std::string(" "), "search for libraries in " }); + list.push_back({ ARG_MCMODEL + std::string(" "), "change the mcmodel of the code" }); + list.push_back({ ARG_DISABLE_AUTO_GLOBAL_CONSTRUCTORS, "disable calling constructors before main()" }); + list.push_back({ ARG_OUTPUT_FILE + std::string(" "), "set the name of the output file" }); + list.push_back({ ARG_OPTIMISATION_LEVEL_SELECT + std::string(""), "change the optimisation level; -O0, -O1, -O2, " + "-O3, and -Ox are valid options" }); + + list.push_back({ ARG_POSINDEPENDENT, "generate position independent code" }); + list.push_back({ ARG_PRINT_FIR, "print the FlaxIR before compilation" }); + list.push_back({ ARG_PRINT_LLVMIR, "print the LLVM IR before compilation" }); + list.push_back({ ARG_PROFILE, "print internal compiler profiling statistics" }); + list.push_back({ ARG_RUNPROGRAM, "use LLVM JIT to run the program, instead of compiling to a file" }); + list.push_back({ ARG_SHOW_CLANG_OUTPUT, "show the output of calling the final compiler" }); + list.push_back({ ARG_SYSROOT + std::string(" "), "set the directory used as the sysroot" }); + list.push_back({ ARG_TARGET + std::string(" "), "change the compilation target" }); + + list.push_back({ WARNING_DISABLE_ALL, "disable all warnings" }); + list.push_back({ WARNINGS_AS_ERRORS, "treat all warnings as errors" }); + + list.push_back({ ARG_NO_RUNTIME_CHECKS, "disable all runtime checks" }); + list.push_back({ ARG_NO_RUNTIME_ERROR_STRINGS, "disable runtime error messages (program will just abort)" }); + + list.push_back({ WARNING_ENABLE_UNUSED_VARIABLE, "enable warnings for unused variables" }); + list.push_back({ WARNING_ENABLE_VARIABLE_CHECKER, "enable warnings from the variable state checker" }); + list.push_back({ WARNING_DISABLE_UNUSED_VARIABLE, "disable warnings for unused variables" }); + list.push_back({ WARNING_DISABLE_VARIABLE_CHECKER, "disable warnings from the variable state checker" }); } static void printHelp() @@ -91,10 +95,10 @@ static void printHelp() if(list.empty()) setupMap(); - printf("Flax Compiler - Version %s\n\n", VERSION_STRING); - printf("Usage: flaxc [options] \n\n"); + printf("Flax Compiler - Version %s\n\n", FLAX_VERSION_STRING); + printf("usage: flaxc [options] \n\n"); - printf("Options:\n"); + printf("options:\n"); size_t maxl = 0; for(auto p : list) @@ -161,9 +165,11 @@ namespace frontend bool _isFreestanding = false; bool _printClangOutput = false; bool _noAutoGlobalConstructor = false; - bool _abortOnError = false; + bool _noRuntimeChecks = false; + bool _noRuntimeErrorStrings = false; + std::string _mcModel; std::string _targetArch; std::string _sysrootPath; @@ -192,6 +198,20 @@ namespace frontend return _backendCodegen; } + bool getPrintProfileStats() + { + return _doProfiler; + } + + bool getIsNoRuntimeChecks() + { + return _noRuntimeChecks; + } + + bool getIsNoRuntimeErrorStrings() + { + return _noRuntimeErrorStrings; + } bool getAbortOnError() { @@ -279,7 +299,7 @@ namespace frontend } else if(!strcmp(argv[i], ARG_VERSION)) { - printf("Flax Compiler (flaxc), version %s\n", VERSION_STRING); + printf("Flax Compiler (flaxc), version %s\n", FLAX_VERSION_STRING); exit(0); } else if(!strcmp(argv[i], ARG_LINK_FRAMEWORK)) @@ -398,6 +418,14 @@ namespace frontend // set freestanding mode frontend::_isFreestanding = true; } + else if(!strcmp(argv[i], ARG_NO_RUNTIME_CHECKS)) + { + frontend::_noRuntimeChecks = true; + } + else if(!strcmp(argv[i], ARG_NO_RUNTIME_ERROR_STRINGS)) + { + frontend::_noRuntimeErrorStrings = true; + } else if(!strcmp(argv[i], ARG_BACKEND)) { if(i != argc - 1) @@ -409,6 +437,10 @@ namespace frontend { frontend::_backendCodegen = BackendOption::LLVM; } + else if(str == "interp") + { + frontend::_backendCodegen = BackendOption::Interpreter; + } else if(str == "x64asm") { frontend::_backendCodegen = BackendOption::Assembly_x64; @@ -600,7 +632,7 @@ namespace frontend // sanity check // what are you even trying to do m8 if(frontend::_outputMode == ProgOutputMode::RunJit && frontend::_isFreestanding) - _error_and_exit("cannot JIT program in freestanding mode"); + _error_and_exit("cannot JIT program in freestanding mode\n"); return { filenames[0], outname }; } diff --git a/source/frontend/import.cpp b/source/frontend/import.cpp index 454b051b..1411b91b 100644 --- a/source/frontend/import.cpp +++ b/source/frontend/import.cpp @@ -1,5 +1,5 @@ // import.cpp -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #include diff --git a/source/frontend/lexer.cpp b/source/frontend/lexer.cpp index ee1662f4..cfca37e1 100644 --- a/source/frontend/lexer.cpp +++ b/source/frontend/lexer.cpp @@ -1,5 +1,5 @@ // lexer.cpp -// Copyright (c) 2014 - 2015, zhiayang@gmail.com +// Copyright (c) 2014 - 2015, zhiayang // Licensed under the Apache License Version 2.0. #include "lexer.h" @@ -386,6 +386,8 @@ namespace lexer unexpected(tok.loc, "'*/'"); } + + // attrs else if(hasPrefix(stream, "@nomangle")) { @@ -411,7 +413,26 @@ namespace lexer tok.text = "@operator"; read = 9; } + else if(hasPrefix(stream, "@platform")) + { + tok.type = TokenType::Attr_Platform; + tok.text = "@platform"; + read = 9; + } + // directives + else if(hasPrefix(stream, "#if")) + { + tok.type = TokenType::Directive_If; + tok.text = "#if"; + read = 3; + } + else if(hasPrefix(stream, "#run")) + { + tok.type = TokenType::Directive_Run; + tok.text = "#run"; + read = 4; + } // unicode stuff @@ -464,6 +485,38 @@ namespace lexer unicodeLength = 1; } + else if(hasPrefix(stream, "'") && stream.size() > 2) + { + tok.type = TokenType::CharacterLiteral; + + if(stream[1] == '\\') + { + switch(stream[2]) + { + case 'n': tok.text = "\n"; break; + case 'b': tok.text = "\b"; break; + case 'a': tok.text = "\a"; break; + case 'r': tok.text = "\r"; break; + case 't': tok.text = "\t"; break; + case '\'': tok.text = "'"; break; + case '\\': tok.text = "\\"; break; + + default: + error(pos, "invalid escape sequence ('\\%c') in character literal", stream[2]); + } + + read = 4; + } + else + { + tok.text = stream.substr(1, 1); + read = 3; + } + + if(stream[read - 1] != '\'') + error(pos, "expected closing '"); + } + // note some special-casing is needed to differentiate between unary +/- and binary +/- // cases where we want binary: // ...) + 3 diff --git a/source/frontend/parser/controlflow.cpp b/source/frontend/parser/controlflow.cpp index 216a9ffc..c537eb4f 100644 --- a/source/frontend/parser/controlflow.cpp +++ b/source/frontend/parser/controlflow.cpp @@ -1,5 +1,5 @@ // controlflow.cpp -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "ast.h" @@ -10,12 +10,14 @@ namespace parser { using TT = lexer::TokenType; - ast::IfStmt* parseIfStmt(State& st) + ast::Stmt* parseIfStmt(State& st) { using Case = ast::IfStmt::Case; auto tok_if = st.eat(); - iceAssert(tok_if == TT::If); + iceAssert(tok_if == TT::If || tok_if == TT::Directive_If); + + bool isStaticIf = tok_if == TT::Directive_If; // of this form: // if(var x = 30; var k = 30; x == k) { ... } else if(cond) { ... } @@ -98,11 +100,15 @@ namespace parser c.body = parseBracedBlock(st); cases.push_back(c); + + if(isStaticIf) c.body->doNotPushNewScope = true; } else if(st.frontAfterWS() == TT::LBrace || st.frontAfterWS() == TT::FatRightArrow) { // ok, parse an else elseCase = parseBracedBlock(st); + if(isStaticIf) elseCase->doNotPushNewScope = true; + break; } else @@ -112,11 +118,24 @@ namespace parser } } - auto ret = util::pool(tok_if.loc); - ret->cases = cases; - ret->elseCase = elseCase; + if(isStaticIf) + { + // compile-time if + auto ret = util::pool(tok_if.loc); + ret->cases = cases; + ret->elseCase = elseCase; - return ret; + return ret; + } + else + { + // normal runtime if + auto ret = util::pool(tok_if.loc); + ret->cases = cases; + ret->elseCase = elseCase; + + return ret; + } } ast::ReturnStmt* parseReturn(State& st) diff --git a/source/frontend/parser/expr.cpp b/source/frontend/parser/expr.cpp index 3a2e35bd..6ab9d967 100644 --- a/source/frontend/parser/expr.cpp +++ b/source/frontend/parser/expr.cpp @@ -1,5 +1,5 @@ // expr.cpp -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "ast.h" @@ -30,7 +30,7 @@ namespace parser } st.pop(); - auto stmt = parseStmt(st); + auto stmt = parseStmt(st, /* allowExprs: */ false); if(auto defn = dcast(FuncDefn, stmt)) defn->visibility = vis; @@ -49,7 +49,7 @@ namespace parser return stmt; } - Stmt* parseStmt(State& st) + Stmt* parseStmt(State& st, bool allowExprs) { if(!st.hasTokens()) unexpected(st, "end of file"); @@ -98,6 +98,7 @@ namespace parser auto tok = st.front(); if(tok != TT::EndOfFile) { + // handle the things that are OK to appear anywhere first: switch(tok.type) { case TT::Var: @@ -110,35 +111,16 @@ namespace parser case TT::ForeignFunc: return parseForeignFunction(st); - - case TT::Public: case TT::Private: case TT::Internal: return parseStmtWithAccessSpec(st); - case TT::If: + case TT::Directive_If: return parseIfStmt(st); - case TT::Else: - error(st, "cannot have 'else' without preceeding 'if'"); - - case TT::Return: - return parseReturn(st); - - case TT::Do: - case TT::While: - case TT::Loop: - return parseWhileLoop(st); - - case TT::For: - return parseForLoop(st); - - case TT::Break: - return parseBreak(st); - - case TT::Continue: - return parseContinue(st); + case TT::Directive_Run: + return parseRunDirective(st); case TT::Attr_Raw: st.eat(); @@ -162,12 +144,6 @@ namespace parser case TT::Static: return parseStaticDecl(st); - case TT::Dealloc: - return parseDealloc(st); - - case TT::Defer: - return parseDefer(st); - case TT::Operator: return parseOperatorOverload(st); @@ -184,6 +160,9 @@ namespace parser case TT::TypeAlias: error(st, "notsup"); + case TT::Else: + error(st, "cannot have 'else' without preceeding 'if'"); + case TT::Namespace: error(st, "namespaces can only be defined at the top-level scope"); @@ -197,10 +176,82 @@ namespace parser error(st, "export declaration must be the first non-comment line in the file"); default: + // do nothing. + break; + } + + + // if we got here, then it wasn't any of those things. + // we store it first, so we can give better error messages (after knowing what it is) + // in the event that it wasn't allowed at top-level. + + Stmt* ret = 0; + switch(tok.type) + { + case TT::If: + ret = parseIfStmt(st); + break; + + case TT::Else: + error(st, "cannot have 'else' without preceeding 'if'"); + + case TT::Return: + ret = parseReturn(st); + break; + + case TT::Do: + case TT::While: + ret = parseWhileLoop(st); + break; + + case TT::For: + ret = parseForLoop(st); + break; + + case TT::Break: + ret = parseBreak(st); + break; + + case TT::Continue: + ret = parseContinue(st); + break; + + case TT::Dealloc: + ret = parseDealloc(st); + break; + + case TT::Defer: + ret = parseDefer(st); + break; + + default: { if(st.isInStructBody() && tok.type == TT::Identifier && tok.str() == "init") - return parseInitFunction(st); + { + ret = parseInitFunction(st); + break; + } + else + { + // we want to error on invalid tokens first. so, we parse the expression regardless, + // then if they're not allowed we error. + auto expr = parseExpr(st); + + if(!allowExprs) error(expr, "expressions are not allowed at the top-level"); + else ret = expr; + + break; + } + } + } - return parseExpr(st); + iceAssert(ret); + if(!st.isInFunctionBody() && !st.isInStructBody()) + { + error(ret, "%s is not allowed at the top-level", ret->readableName); + } + else + { + return ret; } } @@ -636,7 +687,11 @@ namespace parser st.skipWS(); if(st.front().type != TT::Comma && st.front().type != TT::RParen) - expected(opening.loc, "closing ')' to match opening parenthesis here, or ',' to begin a tuple", st.front().str()); + { + SpanError::make(SimpleError::make(st.loc(), "expected ',' to begin a tuple, or a closing ')'"))->add( + util::ESpan(opening.loc, "opening parenthesis was here") + )->append(BareError::make(MsgType::Note, "named tuples are not supported"))->postAndQuit(); + } // if we're a tuple, get ready for this shit. if(st.front().type == TT::Comma) @@ -770,9 +825,9 @@ namespace parser return ret; } - static FunctionCall* parseFunctionCall(State& st, std::string name) + static FunctionCall* parseFunctionCall(State& st, const Location& loc, std::string name) { - auto ret = util::pool(st.lookahead(-2).loc, name); + auto ret = util::pool(loc, name); st.skipWS(); ret->args = parseCallArgumentList(st); @@ -785,7 +840,7 @@ namespace parser { if(Ident* id = dcast(Ident, lhs)) { - auto ret = parseFunctionCall(st, id->name); + auto ret = parseFunctionCall(st, id->loc, id->name); ret->mappings = id->mappings; return ret; @@ -1133,7 +1188,6 @@ namespace parser case TT::Do: case TT::While: - case TT::Loop: case TT::For: error(st, "loops are not expressions"); @@ -1190,6 +1244,10 @@ namespace parser case TT::LSquare: return parseArray(st, false); + case TT::CharacterLiteral: + st.pop(); + return util::pool(tok.loc, (uint32_t) tok.text[0]); + // no point creating separate functions for these case TT::True: st.pop(); @@ -1204,11 +1262,10 @@ namespace parser st.pop(); return util::pool(tok.loc); + case TT::Directive_Run: + return parseRunDirective(st); case TT::LBrace: - // parse it, but throw it away - // warn(parseBracedBlock(st)->loc, "Anonymous blocks are ignored; to run, preface with 'do'"); - // return 0; unexpected(st, "block; to create a nested scope, use 'do { ... }'"); default: diff --git a/source/frontend/parser/function.cpp b/source/frontend/parser/function.cpp index befea371..aece99ec 100644 --- a/source/frontend/parser/function.cpp +++ b/source/frontend/parser/function.cpp @@ -1,5 +1,5 @@ // function.cpp -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "defs.h" @@ -94,7 +94,7 @@ namespace parser } - static std::tuple parseFunctionDecl(State& st) + std::tuple parseFunctionDecl(State& st) { iceAssert(st.front() == TT::Func); st.eat(); diff --git a/source/frontend/parser/literal.cpp b/source/frontend/parser/literal.cpp index f10ae11f..759d3d99 100644 --- a/source/frontend/parser/literal.cpp +++ b/source/frontend/parser/literal.cpp @@ -1,5 +1,5 @@ // literal.cpp -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "defs.h" @@ -32,7 +32,7 @@ namespace parser return util::pool(st.ploc(), t.str()); } - static std::string parseHexEscapes(const Location& loc, std::string_view sv, size_t* ofs) + static std::string parseHexEscapes(const Location& loc, util::string_view sv, size_t* ofs) { if(sv[0] == 'x') { @@ -44,7 +44,7 @@ namespace parser // ok then. char s[2] = { sv[1], sv[2] }; - char val = std::stol(s, /* pos: */ 0, /* base: */ 16); + char val = (char) std::stol(s, /* pos: */ 0, /* base: */ 16); *ofs = 3; return std::string(&val, 1); @@ -92,6 +92,7 @@ namespace parser else { iceAssert("wtf yo" && 0); + return ""; } } @@ -116,7 +117,7 @@ namespace parser case 'x': // fallthrough case 'u': { size_t ofs = 0; - ss << parseHexEscapes(loc, std::string_view(str.c_str() + i, str.size() - i), &ofs); + ss << parseHexEscapes(loc, util::string_view(str.c_str() + i, str.size() - i), &ofs); i += ofs - 1; break; } diff --git a/source/frontend/parser/misc.cpp b/source/frontend/parser/misc.cpp index d3e006bd..e5619a20 100644 --- a/source/frontend/parser/misc.cpp +++ b/source/frontend/parser/misc.cpp @@ -1,5 +1,5 @@ // misc.cpp -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "frontend.h" @@ -78,6 +78,144 @@ namespace parser ret->useAs = st.eat().str(); return ret; } + + + + RunDirective* parseRunDirective(State& st) + { + iceAssert(st.front() == TT::Directive_Run); + + auto ret = util::pool(st.eat().loc); + + // trick the parser. when we do a #run, we will run it in a function wrapper, so we + // are technically "inside" a function. + st.enterFunctionBody(); + defer(st.leaveFunctionBody()); + + if(st.front() == TT::LBrace) ret->block = parseBracedBlock(st); + else ret->insideExpr = parseExpr(st); + + return ret; + } + + + + + PlatformDefn* parsePlatformDefn(State& st) + { + iceAssert(st.front() == TT::Attr_Platform); + auto l = st.loc(); + + st.eat(); + + if(st.eat() != TT::LSquare) + expectedAfter(st.ploc(), "'['", "@platform definition", st.prev().str()); + + PlatformDefn* pd = util::pool(l); + + // see what the thing is. + if(st.front() == TT::Identifier && st.front().str() == "intrinsic") + { + st.eat(); + pd->defnType = PlatformDefn::Type::Intrinsic; + + if(st.eat() != TT::Comma) + expected(st.ploc(), "',' in argument list to @platform", st.prev().str()); + + if(st.front() != TT::StringLiteral) + expected(st.loc(), "string literal to specify intrinsic name", st.front().str()); + + auto realname = st.eat().str(); + + if(st.eat() != TT::RSquare) + expectedAfter(st.ploc(), "']'", "@platform definition", st.prev().str()); + + if(st.front() != TT::Func) + expectedAfter(st.loc(), "function declaration", "@platform", st.front().str()); + + auto [ defn, isvar, varloc ] = parseFunctionDecl(st); + (void) varloc; + + if(!defn->generics.empty()) + error(defn->loc, "platform intrinsics cannot be generic"); + + auto ffn = util::pool(st.loc()); + ffn->realName = realname; + + ffn->loc = defn->loc; + ffn->isVarArg = isvar; + ffn->args = defn->args; + ffn->name = defn->name; + ffn->visibility = defn->visibility; + ffn->returnType = defn->returnType; + + + pd->intrinsicDefn = ffn; + } + else if(st.front() == TT::Identifier && st.front().str() == "integer_type") + { + st.eat(); + pd->defnType = PlatformDefn::Type::IntegerType; + + if(st.eat() != TT::Comma) + expected(st.ploc(), "',' in argument list to @platform", st.prev().str()); + + auto num = st.front().str(); + if(st.front() != TT::Number || num.find('.') != std::string::npos) + expected(st.ploc(), "integer value to specify type size (in bits)", st.front().str()); + + st.eat(); + + int sz = std::stoi(num); + if(sz <= 0) expected(st.ploc(), "non-zero and non-negative size", num); + else if(sz < 8) error(st.ploc(), "types less than 8-bits wide are currently not supported"); + + pd->typeSizeInBits = sz; + + if(st.eat() != TT::RSquare) + expectedAfter(st.ploc(), "']'", "@platform definition", st.prev().str()); + + if(st.front() != TT::Identifier) + expectedAfter(st.loc(), "identifier as type name", "@platform definition", st.front().str()); + + pd->typeName = st.eat().str(); + } + else if(st.front() == TT::Identifier && st.front().str() == "native_word_size") + { + if(!st.nativeWordSizeStillValid) + { + SimpleError::make(st.loc(), "setting the native word size is no longer possible at this point")->append( + BareError::make(MsgType::Note, "@platform[native_word_size] must appear before any code declarations, " + "and be the first '@platform' declaration"))->postAndQuit(); + } + + st.eat(); + + if(st.eat() != TT::RSquare) + expectedAfter(st.ploc(), "']'", "@platform definition", st.prev().str()); + + auto num = st.front().str(); + if(st.front() != TT::Number || num.find('.') != std::string::npos) + expected(st.ploc(), "integer value to specify word size (in bits)", st.front().str()); + + st.eat(); + + int sz = std::stoi(num); + if(sz <= 0) expected(st.ploc(), "non-zero and non-negative size", num); + else if(sz < 8) error(st.ploc(), "types less than 8-bits wide are currently not supported"); + + //? should we warn if it was already set? + st.cState->nativeWordSize = sz; + + return 0; + } + else + { + error(st.loc(), "invalid platform declaration of type '%s'", st.front().str()); + } + + return pd; + } } void expected(const Location& loc, std::string a, std::string b) diff --git a/source/frontend/parser/operators.cpp b/source/frontend/parser/operators.cpp index d3ea5607..8489d590 100644 --- a/source/frontend/parser/operators.cpp +++ b/source/frontend/parser/operators.cpp @@ -1,5 +1,5 @@ // operators.cpp -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "defs.h" @@ -53,8 +53,12 @@ namespace parser if(st.front() != TT::LBrace && st.front() != TT::FatRightArrow) expected(st, "'{' to begin function body", st.front().str()); + st.enterFunctionBody(); + ret->body = parseBracedBlock(st); ret->name = ret->symbol; + + st.leaveFunctionBody(); return ret; } @@ -167,11 +171,15 @@ namespace parser CustomOperatorDecl oper; oper.loc = tok.loc; + idx++; + if(tokens[idx] != TT::LSquare) + expectedAfter(tokens[idx].loc, "'['", "'@operator' in custom operator declaration", tokens[idx].str()); + idx++; if(tokens[idx].str() != "prefix" && tokens[idx].str() != "postfix" && tokens[idx].str() != "infix") { - expectedAfter(tokens[idx].loc, "either 'prefix', 'postfix' or 'infix'", "'operator' in custom operator declaration", + expectedAfter(tokens[idx].loc, "either 'prefix', 'postfix' or 'infix'", "'@operator' in custom operator declaration", tokens[idx].str()); } @@ -182,14 +190,15 @@ namespace parser else iceAssert(0); idx++; + if(tokens[idx] != TT::Comma) + expected(tokens[idx].loc, "',' in argument list to '@operator'", tokens[idx].str()); - { - if(tokens[idx] != TT::Number) - expectedAfter(tokens[idx].loc, "integer value", "to specify precedence value", tokens[idx].str()); + idx++; - // make sure it's an integer. + { auto num = tokens[idx].str(); - if(num.find('.') != std::string::npos) + + if(tokens[idx] != TT::Number || num.find('.') != std::string::npos) expected(tokens[idx].loc, "integer value for precedence", num); int prec = std::stoi(num); @@ -200,19 +209,29 @@ namespace parser idx++; } + if(tokens[idx] != TT::Comma) + expected(tokens[idx].loc, "',' in argument list to '@operator'", tokens[idx].str()); - oper.symbol = tokens[idx].str(); idx++; - - if(tokens[idx] != TT::NewLine && tokens[idx] != TT::Semicolon && tokens[idx] != TT::Comment) + if(tokens[idx] != TT::UnicodeSymbol && tokens[idx] != TT::Identifier) { - error(tokens[idx].loc, "expected newline or semicolon to terminate operator declaration, found '%s'", tokens[idx].str()); + error(tokens[idx].loc, "custom operator symbol must be a unicode symbol or regular identifier, '%s' is invalid", + tokens[idx].str()); } - if(_kind) *_kind = kind; - if(out) *out = oper; + oper.symbol = tokens[idx].str(); + idx++; + + if(tokens[idx] != TT::RSquare) + error(tokens[idx].loc, "expected ']' to terminate operator declaration, found '%s'", tokens[idx].str()); + + idx++; + + + if(_kind) *_kind = kind; + if(out) *out = oper; return idx; } diff --git a/source/frontend/parser/toplevel.cpp b/source/frontend/parser/toplevel.cpp index 8e8a4d08..335b3426 100644 --- a/source/frontend/parser/toplevel.cpp +++ b/source/frontend/parser/toplevel.cpp @@ -1,5 +1,5 @@ // toplevel.cpp -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "pts.h" @@ -119,31 +119,38 @@ namespace parser size_t tix = (size_t) -1; - // flags that determine whether or not 'import' and '@operator' things can still be done. - bool importsStillValid = true; - bool operatorsStillValid = true; - - while(st.hasTokens() && st.front() != TT::EndOfFile) { switch(st.front()) { - case TT::Import: - if(name != "" || !importsStillValid) + case TT::Import: { + if(name != "" || !st.importsStillValid) error(st, "import statements are not allowed here"); root->statements.push_back(parseImport(st)); - break; + } break; - case TT::Attr_Operator: - if(name != "" || !operatorsStillValid) + case TT::Attr_Operator: { + if(name != "" || !st.operatorsStillValid) error(st, "custom operator declarations are not allowed here"); // just skip it. st.setIndex(parseOperatorDecl(st.getTokenList(), st.getIndex(), 0, 0)); - importsStillValid = false; - break; + st.importsStillValid = false; + st.nativeWordSizeStillValid = false; + } break; + + case TT::Attr_Platform: { + + auto ret = parsePlatformDefn(st); + + if(ret) // sometimes we set a setting, but it doesn't need to have an AST node. + root->statements.push_back(ret); + + st.importsStillValid = false; + st.nativeWordSizeStillValid = false; + } break; case TT::Namespace: { st.eat(); @@ -157,8 +164,9 @@ namespace parser root->statements.push_back(ns); - importsStillValid = false; - operatorsStillValid = false; + st.importsStillValid = false; + st.operatorsStillValid = false; + st.nativeWordSizeStillValid = false; } break; @@ -179,8 +187,9 @@ namespace parser root->statements.push_back(stmt); - importsStillValid = false; - operatorsStillValid = false; + st.importsStillValid = false; + st.operatorsStillValid = false; + st.nativeWordSizeStillValid = false; } break; @@ -195,8 +204,9 @@ namespace parser root->statements.push_back(stmt); - importsStillValid = false; - operatorsStillValid = false; + st.importsStillValid = false; + st.operatorsStillValid = false; + st.nativeWordSizeStillValid = false; } break; @@ -225,10 +235,10 @@ namespace parser continue; case TT::RBrace: - if(!hadLBrace) error(st, "Unexpected '}'"); + if(!hadLBrace) error(st, "unexpected '}'"); goto out; - default: + default: { if(priv != VisibilityLevel::Invalid) { st.rewindTo(tix); @@ -255,11 +265,12 @@ namespace parser } } - importsStillValid = false; - operatorsStillValid = false; + st.importsStillValid = false; + st.operatorsStillValid = false; + st.nativeWordSizeStillValid = false; - root->statements.push_back(parseStmt(st)); - break; + root->statements.push_back(parseStmt(st, /* allowExprs: */ false)); + } break; } isFirst = false; @@ -293,6 +304,8 @@ namespace parser state.prefixOps = cs.prefixOps; state.postfixOps = cs.postfixOps; + state.cState = &cs; + auto [ modname, modpath ] = parseModuleName(full); auto toplevel = parseTopLevel(state, ""); diff --git a/source/frontend/parser/type.cpp b/source/frontend/parser/type.cpp index 780a8b76..80eacd7a 100644 --- a/source/frontend/parser/type.cpp +++ b/source/frontend/parser/type.cpp @@ -1,5 +1,5 @@ // type.cpp -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "pts.h" @@ -129,7 +129,7 @@ namespace parser StructDefn* defn = util::pool(st.loc()); if(nameless) { - defn->name = strprintf("__anon_struct_%zu", anon_counter++); + defn->name = util::obfuscateName("anon_struct", anon_counter++); } else { @@ -243,7 +243,7 @@ namespace parser UnionDefn* defn = util::pool(st.loc()); if(nameless) { - defn->name = strprintf("__anon_union_%zu", anon_counter++); + defn->name = util::obfuscateName("anon_union", anon_counter++); } else { @@ -709,6 +709,7 @@ namespace parser PolyArgMapping_t parsePAMs(State& st, bool* failed) { iceAssert(st.front() == TT::Exclamation && st.lookahead(1) == TT::LAngle); + auto openLoc = st.loc(); st.pop(); st.pop(); @@ -718,7 +719,7 @@ namespace parser { //* foo!<> is an error regardless of whether we're doing expression parsing or call parsing. if(st.front() == TT::RAngle) - error(st.loc(), "at least one type argument is required between angle brackets <>"); + error(Location::unionOf(openLoc, st.loc()), "type parameter list cannot be empty"); // step 2A: start parsing. size_t idx = 0; diff --git a/source/frontend/parser/variable.cpp b/source/frontend/parser/variable.cpp index 75886039..cf8849ff 100644 --- a/source/frontend/parser/variable.cpp +++ b/source/frontend/parser/variable.cpp @@ -1,5 +1,5 @@ // variable.cpp -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "pts.h" diff --git a/source/frontend/platform.cpp b/source/frontend/platform.cpp index 93c7b9f3..90364fe5 100644 --- a/source/frontend/platform.cpp +++ b/source/frontend/platform.cpp @@ -1,5 +1,5 @@ // platform.cpp -// Copyright (c) 2017, zhiayang@gmail.com +// Copyright (c) 2017, zhiayang // Licensed under the Apache License Version 2.0. #include @@ -8,6 +8,7 @@ #include "frontend.h" #ifndef _WIN32 + #include #include #include #include @@ -23,6 +24,14 @@ #define EXTRA_MMAP_FLAGS 0 #endif #else + #define WIN32_LEAN_AND_MEAN 1 + + #ifndef NOMINMAX + #define NOMINMAX + #endif + + #include + #define USE_MMAP false #endif @@ -31,10 +40,68 @@ namespace platform { #ifdef _WIN32 filehandle_t InvalidFileHandle = INVALID_HANDLE_VALUE; + + static HMODULE currentModule = 0; + static std::vector otherModules; #else filehandle_t InvalidFileHandle = -1; + void* currentModule = 0; #endif + + void performSelfDlOpen() + { + #ifdef _WIN32 + currentModule = GetModuleHandle(nullptr); + + // load the libc dll. + //? the name is "vcruntime140.dll", which is apparently specific to MSVC 14.0+, apparently 140 is the only number that + //? appears to be referenced in online sources. + + std::vector libsToLoad = { + "vcruntime140.dll", + "ucrtbase.dll" + }; + + for(auto name : libsToLoad) + { + auto lib = LoadLibrary(name); + if(lib) otherModules.push_back(lib); + else warn("platform: failed to load library '%s'", name); + } + #else + currentModule = dlopen(nullptr, RTLD_LAZY); + #endif + } + + void performSelfDlClose() + { + #ifdef _WIN32 + for(auto mod : otherModules) + FreeLibrary(mod); + #endif + } + + void* getSymbol(const std::string& name) + { + if(!currentModule) _error_and_exit("failed to load current module!\n"); + + void* ret = 0; + #ifdef _WIN32 + ret = GetProcAddress(currentModule, name.c_str()); + for(size_t i = 0; !ret && i < otherModules.size(); i++) + ret = GetProcAddress(otherModules[i], name.c_str()); + #else + ret = dlsym(currentModule, name.c_str()); + #endif + + return ret; + } + + + + + size_t getFileSize(const std::string& path) { #ifdef _WIN32 @@ -43,13 +110,13 @@ namespace platform HANDLE hd = CreateFile((LPCSTR) path.c_str(), GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); if(hd == INVALID_HANDLE_VALUE) - _error_and_exit("failed to get filesize for '%s' (error code %d)", path, GetLastError()); + _error_and_exit("failed to get filesize for '%s' (error code %d)\n", path, GetLastError()); // ok, presumably it exists. so, get the size LARGE_INTEGER sz; bool success = GetFileSizeEx(hd, &sz); if(!success) - _error_and_exit("failed to get filesize for '%s' (error code %d)", path, GetLastError()); + _error_and_exit("failed to get filesize for '%s' (error code %d)\n", path, GetLastError()); CloseHandle(hd); @@ -59,7 +126,7 @@ namespace platform struct stat st; if(stat(path.c_str(), &st) != 0) - _error_and_exit("failed to get filesize for '%s' (error code %d / %s)", path, errno, strerror(errno)); + _error_and_exit("failed to get filesize for '%s' (error code %d / %s)\n", path, errno, strerror(errno)); return st.st_size; @@ -139,7 +206,6 @@ namespace platform if(hd == INVALID_HANDLE_VALUE) return platform::InvalidFileHandle; - // _error_and_exit("Failed to open file '%s' (error code %d)", name, GetLastError()); return hd; #else @@ -164,9 +230,9 @@ namespace platform { #ifdef _WIN32 DWORD didRead = 0; - bool success = ReadFile(fd, buf, (platform::DWORD) count, &didRead, 0); + bool success = ReadFile(fd, buf, (DWORD) count, &didRead, 0); if(!success) - _error_and_exit("failed to read file (wanted %d bytes, read %d bytes); (error code %d)", count, didRead, GetLastError()); + _error_and_exit("failed to read file (wanted %d bytes, read %d bytes); (error code %d)\n", count, didRead, GetLastError()); return (size_t) didRead; #else @@ -178,9 +244,9 @@ namespace platform { #ifdef _WIN32 DWORD didWrite = 0; - bool success = WriteFile(fd, buf, (platform::DWORD) count, &didWrite, 0); + bool success = WriteFile(fd, buf, (DWORD) count, &didWrite, 0); if(!success) - _error_and_exit("failed to write file (wanted %d bytes, wrote %d bytes); (error code %d)", count, didWrite, GetLastError()); + _error_and_exit("failed to write file (wanted %d bytes, wrote %d bytes); (error code %d)\n", count, didWrite, GetLastError()); return (size_t) didWrite; #else @@ -225,7 +291,6 @@ namespace platform HANDLE hd = CreateFile((LPCSTR) p.c_str(), GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); if(hd == INVALID_HANDLE_VALUE) return ""; - // _error_and_exit("Failed to open file '%s' (error code %d)", partial, GetLastError()); // ok, presumably it exists. defer(CloseHandle(hd)); @@ -242,7 +307,6 @@ namespace platform } else { - // _error_and_exit("Failed to open file '%s' (error code %d)", partial, GetLastError()); return ""; } } diff --git a/source/frontend/pts.cpp b/source/frontend/pts.cpp index ed0123bf..ed650937 100644 --- a/source/frontend/pts.cpp +++ b/source/frontend/pts.cpp @@ -1,8 +1,9 @@ // pts.cpp -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "pts.h" +#include "mpool.h" namespace pts { @@ -124,7 +125,7 @@ namespace pts InferredType* InferredType::get() { if(it) return it; - return (it = new InferredType(Location())); + return (it = util::pool(Location())); } NamedType* NamedType::create(const Location& l, const std::string& s) @@ -134,7 +135,7 @@ namespace pts NamedType* NamedType::create(const Location& l, const std::string& s, const PolyArgMapping_t& tm) { - auto ret = new NamedType(l, s); + auto ret = util::pool(l, s); ret->genericMapping = tm; return ret; diff --git a/source/include/allocator.h b/source/include/allocator.h index 51e74a38..e223d4a2 100644 --- a/source/include/allocator.h +++ b/source/include/allocator.h @@ -1,5 +1,5 @@ // allocator.h -// Copyright (c) 2017, zhiayang@gmail.com +// Copyright (c) 2017, zhiayang // Licensed under the Apache License Version 2.0. #pragma once diff --git a/source/include/ast.h b/source/include/ast.h index 4c897cbe..8a372b96 100644 --- a/source/include/ast.h +++ b/source/include/ast.h @@ -1,5 +1,5 @@ // ast.h -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #pragma once @@ -123,6 +123,8 @@ namespace ast Location closingBrace; + bool doNotPushNewScope = false; + bool isArrow = false; bool isFunctionBody = false; std::vector statements; @@ -131,7 +133,6 @@ namespace ast - struct FuncDefn : Parameterisable { FuncDefn(const Location& l) : Parameterisable(l) { this->readableName = "function defintion"; } @@ -201,6 +202,7 @@ namespace ast pts::Type* returnType = 0; bool isVarArg = false; + bool isIntrinsic = false; //* note: foriegn functions are not Parameterisable, so they don't have the 'visibility' -- so we add it. VisibilityLevel visibility = VisibilityLevel::Internal; @@ -258,6 +260,43 @@ namespace ast DecompMapping bindings; }; + + struct PlatformDefn : Stmt + { + PlatformDefn(const Location& l) : Stmt(l) { this->readableName = "platform-specific definition"; } + ~PlatformDefn() { } + + virtual TCResult typecheck(sst::TypecheckState* fs, fir::Type* infer = 0) override; + + enum class Type + { + Invalid, + Intrinsic, + IntegerType + }; + + Type defnType = Type::Invalid; + + // only valid if defnType == Intrinsic + ForeignFuncDefn* intrinsicDefn = 0; + + // only valid if defnType == IntegerType + std::string typeName; + size_t typeSizeInBits = 0; + }; + + + + + + + + + + + + + struct IfStmt : Stmt { IfStmt(const Location& l) : Stmt(l) { this->readableName = "if statement"; } @@ -699,6 +738,16 @@ namespace ast std::string num; }; + struct LitChar : Expr + { + LitChar(const Location& l, uint32_t val) : Expr(l), value(val) { this->readableName = "character literal"; } + ~LitChar() { } + + virtual TCResult typecheck(sst::TypecheckState* fs, fir::Type* infer = 0) override; + + uint32_t value = false; + }; + struct LitBool : Expr { LitBool(const Location& l, bool val) : Expr(l), value(val) { this->readableName = "boolean literal"; } @@ -751,6 +800,32 @@ namespace ast }; + + struct RunDirective : Expr + { + RunDirective(const Location& l) : Expr(l) { this->readableName = "#run directive"; } + ~RunDirective() { } + + virtual TCResult typecheck(sst::TypecheckState* fs, fir::Type* infer = 0) override; + + // note: these two are mutually exclusive! + Block* block = 0; + Expr* insideExpr = 0; + }; + + struct IfDirective : Stmt + { + IfDirective(const Location& l) : Stmt(l) { this->readableName = "#if directive"; } + ~IfDirective() { } + + virtual TCResult typecheck(sst::TypecheckState* fs, fir::Type* infer = 0) override; + + std::vector cases; + Block* elseCase = 0; + }; + + + struct TopLevelBlock : Stmt { TopLevelBlock(const Location& l, std::string n) : Stmt(l), name(n) { this->readableName = "namespace"; } diff --git a/source/include/backend.h b/source/include/backend.h index 864daa1e..f7006722 100644 --- a/source/include/backend.h +++ b/source/include/backend.h @@ -1,5 +1,5 @@ // backend.h -// Copyright (c) 2014 - 2016, zhiayang@gmail.com +// Copyright (c) 2014 - 2016, zhiayang // Licensed under the Apache License Version 2.0. #pragma once @@ -59,6 +59,7 @@ namespace backend None, LLVM, + Interpreter, Assembly_x64, }; diff --git a/source/include/backends/interp.h b/source/include/backends/interp.h new file mode 100644 index 00000000..3f8d1a81 --- /dev/null +++ b/source/include/backends/interp.h @@ -0,0 +1,28 @@ +// interp.h +// Copyright (c) 2019, zhiayang +// Licensed under the Apache License Version 2.0. + +#include "backend.h" + +namespace fir::interp +{ + struct InterpState; +} + +namespace backend +{ + struct FIRInterpBackend : Backend + { + FIRInterpBackend(CompiledData& dat, std::vector inputs, std::string output); + virtual ~FIRInterpBackend() { } + + virtual void performCompilation() override; + virtual void optimiseProgram() override; + virtual void writeOutput() override; + + virtual std::string str() override; + + protected: + fir::interp::InterpState* is = 0; + }; +} diff --git a/source/include/backends/llvm.h b/source/include/backends/llvm.h index 750751d6..30e94805 100644 --- a/source/include/backends/llvm.h +++ b/source/include/backends/llvm.h @@ -1,5 +1,5 @@ // llvm.h -// Copyright (c) 2017, zhiayang@gmail.com +// Copyright (c) 2017, zhiayang // Licensed under the Apache License Version 2.0. #pragma once @@ -99,7 +99,6 @@ namespace backend private: void setupTargetMachine(); - void finaliseGlobalConstructors(); EntryPoint_t getEntryFunctionFromJIT(); llvm::SmallVector initialiseLLVMStuff(); diff --git a/source/include/codegen.h b/source/include/codegen.h index dd270bd1..dc65c596 100644 --- a/source/include/codegen.h +++ b/source/include/codegen.h @@ -1,5 +1,5 @@ // codegen.h -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #pragma once @@ -12,6 +12,11 @@ namespace fir { struct Module; struct IRBuilder; + + namespace interp + { + struct InterpState; + } } namespace sst @@ -64,11 +69,10 @@ namespace cgn UserDefined }; + CodegenState(const fir::IRBuilder& i); - - CodegenState(const fir::IRBuilder& i) : irb(i) { } + size_t id = 0; fir::Module* module = 0; - sst::StateTree* stree = 0; fir::IRBuilder irb; @@ -92,6 +96,7 @@ namespace cgn void popIRDebugIndentation(); + void pushLoc(const Location& l); void pushLoc(sst::Stmt* stmt); void popLoc(); @@ -186,7 +191,7 @@ namespace cgn void autoAssignRefCountedValue(fir::Value* lhs, fir::Value* rhs, bool isInitial, bool performStore); }; - fir::Module* codegen(sst::DefinitionTree* dtr); + fir::Module* codegen(sst::DefinitionTree* __std_exception_destroy); } diff --git a/source/include/container.h b/source/include/container.h index b3247c6e..10a19709 100644 --- a/source/include/container.h +++ b/source/include/container.h @@ -1,5 +1,5 @@ // container.h -// Copyright (c) 2017, zhiayang@gmail.com +// Copyright (c) 2017, zhiayang // Licensed under the Apache License Version 2.0. #pragma once diff --git a/source/include/defs.h b/source/include/defs.h index 4f15104f..d737cd1e 100644 --- a/source/include/defs.h +++ b/source/include/defs.h @@ -1,5 +1,5 @@ // defs.h -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #pragma once @@ -80,7 +80,7 @@ std::string strprintf(const char* fmt, Ts... ts) #ifdef NDEBUG #define iceAssert(x) ((void) (x)) #else -#define iceAssert(x) ((x) ? ((void) (0)) : _error_and_exit("Compiler assertion at %s:%d, cause:\n'%s' evaluated to false\n", __FILE__, __LINE__, #x)) +#define iceAssert(x) ((x) ? ((void) (0)) : _error_and_exit("compiler assertion at %s:%d, cause:\n'%s' evaluated to false\n", __FILE__, __LINE__, #x)) #endif #define TAB_WIDTH 4 @@ -211,6 +211,30 @@ struct Location return !(*this == other); } + static Location unionOf(const Location& a, const Location& b) + { + if(a.fileID != b.fileID || a.line != b.line) + return a; + + Location ret; + ret.fileID = a.fileID; + ret.line = a.line; + + auto end = std::max(a.col + a.len, b.col + b.len); + if(a.col <= b.col) + { + ret.col = a.col; + } + else if(b.col < a.col) + { + ret.col = b.col; + } + + ret.len = (end - ret.col); + + return ret; + } + std::string toString() const; std::string shortString() const; }; @@ -229,7 +253,7 @@ struct BareError; struct SpanError; struct SimpleError; struct OverloadError; - +struct ExampleMsg; //? in order of complexity, i guess? enum class ErrKind @@ -239,6 +263,8 @@ enum class ErrKind Span, // error with context, but with squiggles within those (general case of Overload, // most complex one; built specifically to handle multiple candidates and such + + Example, // same as a SimpleError, but we give the "context" as a string. }; enum class MsgType @@ -267,6 +293,7 @@ namespace util SpanError* make_SpanError(SimpleError* se, const std::vector& s = { }, MsgType t = MsgType::Error); SimpleError* make_SimpleError(const Location& l, const std::string& m, MsgType t = MsgType::Error); OverloadError* make_OverloadError(SimpleError* se, MsgType t = MsgType::Error); + ExampleMsg* make_ExampleMsg(const std::string& eg, MsgType t = MsgType::Note); } @@ -305,10 +332,6 @@ struct BareError : ErrorMsg template static BareError* make(MsgType t, const char* fmt, Ts... ts) { return util::make_BareError(strprintf(fmt, ts...), t); } - - - - virtual void post() override; virtual BareError* append(ErrorMsg* e) override { this->subs.push_back(e); return this; } virtual BareError* prepend(ErrorMsg* e) override { this->subs.insert(this->subs.begin(), e); return this; } @@ -353,6 +376,27 @@ struct SimpleError : ErrorMsg friend struct util::FastInsertVector; }; +struct ExampleMsg : ErrorMsg +{ + static ExampleMsg* make(const std::string& eg) { return util::make_ExampleMsg(eg); } + + virtual void post() override; + virtual ExampleMsg* append(ErrorMsg* e) override { this->subs.push_back(e); return this; } + virtual ExampleMsg* prepend(ErrorMsg* e) override { this->subs.insert(this->subs.begin(), e); return this; } + + std::string example; + + protected: + ExampleMsg() : ErrorMsg(ErrKind::Example, MsgType::Note) { } + ExampleMsg(const std::string& eg, MsgType t) : ErrorMsg(ErrKind::Example, t), example(eg) { } + + friend struct util::MemoryPool; + friend struct util::FastInsertVector; +}; + + + + struct SpanError : ErrorMsg { SpanError* add(const util::ESpan& s); @@ -594,6 +638,8 @@ struct PolyArgMapping_t void add(const std::string& name, pts::Type* t); void add(size_t idx, pts::Type* t); + std::string print() const; + static inline PolyArgMapping_t none() { return PolyArgMapping_t(); } }; @@ -607,6 +653,13 @@ namespace util std::string typeParamMapToString(const std::string& name, const TypeParamMap_t& map); + std::string obfuscateName(const std::string& name); + std::string obfuscateName(const std::string& name, size_t id); + std::string obfuscateName(const std::string& name, const std::string& extra); + Identifier obfuscateIdentifier(const std::string& name, IdKind kind = IdKind::Name); + Identifier obfuscateIdentifier(const std::string& name, size_t id, IdKind kind = IdKind::Name); + Identifier obfuscateIdentifier(const std::string& name, const std::string& extra, IdKind kind = IdKind::Name); + template std::string listToEnglish(const std::vector& list, bool quote = true) { diff --git a/source/include/errors.h b/source/include/errors.h index e835c585..6c3beb3a 100644 --- a/source/include/errors.h +++ b/source/include/errors.h @@ -1,5 +1,5 @@ // errors.h -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #pragma once @@ -95,14 +95,14 @@ std::string __error_gen(const Location& loc, const char* msg, const char* type, } -#define ERROR_FUNCTION(name, type, attr, doexit) \ -template [[attr]] void name (const char* fmt, Ts... ts) \ -{ fputs(__error_gen(Location(), fmt, type, doexit, ts...).c_str(), stderr); if(doexit) doTheExit(false); } \ - \ -template [[attr]] void name (Locatable* e, const char* fmt, Ts... ts) \ -{ fputs(__error_gen(e ? e->loc : Location(), fmt, type, doexit, ts...).c_str(), stderr); if(doexit) doTheExit(false); } \ - \ -template [[attr]] void name (const Location& loc, const char* fmt, Ts... ts) \ +#define ERROR_FUNCTION(name, type, attr, doexit) \ +template [[attr]] void name (const char* fmt, Ts... ts) \ +{ fputs(__error_gen(Location(), fmt, type, doexit, ts...).c_str(), stderr); if(doexit) doTheExit(false); } \ + \ +template [[attr]] void name (Locatable* e, const char* fmt, Ts... ts) \ +{ fputs(__error_gen(e ? e->loc : Location(), fmt, type, doexit, ts...).c_str(), stderr); if(doexit) doTheExit(false); } \ + \ +template [[attr]] void name (const Location& loc, const char* fmt, Ts... ts) \ { fputs(__error_gen(loc, fmt, type, doexit, ts...).c_str(), stderr); if(doexit) doTheExit(false); } diff --git a/source/include/frontend.h b/source/include/frontend.h index 16ecce9b..18601615 100644 --- a/source/include/frontend.h +++ b/source/include/frontend.h @@ -1,5 +1,5 @@ // frontend.h -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #pragma once @@ -45,10 +45,15 @@ namespace frontend bool getPrintFIR(); bool getPrintLLVMIR(); + bool getPrintProfileStats(); bool getAbortOnError(); bool getIsFreestanding(); bool getIsPositionIndependent(); + + bool getIsNoRuntimeChecks(); + bool getIsNoRuntimeErrorStrings(); + std::vector getFrameworksToLink(); std::vector getFrameworkSearchPaths(); std::vector getLibrariesToLink(); @@ -68,6 +73,8 @@ namespace frontend DependencyGraph* graph = 0; std::string fullMainFile; std::vector allFiles; + + size_t nativeWordSize = 0; }; // fir::Module* collectFiles(std::string filename); diff --git a/source/include/gluecode.h b/source/include/gluecode.h index b3651c1b..ce859bd2 100644 --- a/source/include/gluecode.h +++ b/source/include/gluecode.h @@ -1,5 +1,5 @@ // gluecode.h -// Copyright (c) 2017, zhiayang@gmail.com +// Copyright (c) 2017, zhiayang // Licensed under the Apache License Version 2.0. #pragma once @@ -53,6 +53,7 @@ #define BUILTIN_ANY_DATA_BYTECOUNT 32 #define BUILTIN_ANY_FLAG_MASK 0x8000000000000000 +#define BUILTIN_GLOBAL_INIT_FUNCTION_NAME "global_init_function__" namespace fir @@ -146,6 +147,38 @@ namespace cgn { fir::Function* getMallocWrapperFunction(CodegenState* cs); fir::Function* getRangeSanityCheckFunction(CodegenState* cs); + + Identifier getCompare_FName(fir::Type* t); + Identifier getSetElements_FName(fir::Type* t); + Identifier getSetElementsDefault_FName(fir::Type* t); + Identifier getCallClassConstructor_FName(fir::Type* t); + + Identifier getClone_FName(fir::Type* t); + Identifier getAppend_FName(fir::Type* t); + Identifier getPopBack_FName(fir::Type* t); + Identifier getMakeFromTwo_FName(fir::Type* t); + Identifier getMakeFromOne_FName(fir::Type* t); + Identifier getReserveExtra_FName(fir::Type* t); + Identifier getAppendElement_FName(fir::Type* t); + Identifier getReserveEnough_FName(fir::Type* t); + Identifier getRecursiveRefcount_FName(fir::Type* t, bool incr); + + Identifier getIncrRefcount_FName(fir::Type* t); + Identifier getDecrRefcount_FName(fir::Type* t); + + Identifier getLoopIncrRefcount_FName(fir::Type* t); + Identifier getLoopDecrRefcount_FName(fir::Type* t); + + Identifier getCreateAnyOf_FName(fir::Type* t); + Identifier getGetValueFromAny_FName(fir::Type* t); + + Identifier getBoundsCheck_FName(); + Identifier getDecompBoundsCheck_FName(); + + Identifier getMallocWrapper_FName(); + Identifier getRangeSanityCheck_FName(); + + Identifier getUtf8Length_FName(); } } } diff --git a/source/include/ir/block.h b/source/include/ir/block.h index 80929fd7..6cd7df9b 100644 --- a/source/include/ir/block.h +++ b/source/include/ir/block.h @@ -1,5 +1,5 @@ // block.h -// Copyright (c) 2014 - 2016, zhiayang@gmail.com +// Copyright (c) 2014 - 2016, zhiayang // Licensed under the Apache License Version 2.0. #pragma once diff --git a/source/include/ir/constant.h b/source/include/ir/constant.h index 317e1835..2ad9e265 100644 --- a/source/include/ir/constant.h +++ b/source/include/ir/constant.h @@ -1,5 +1,5 @@ // constant.h -// Copyright (c) 2014 - 2016, zhiayang@gmail.com +// Copyright (c) 2014 - 2016, zhiayang // Licensed under the Apache License Version 2.0. #pragma once @@ -84,6 +84,8 @@ namespace fir static ConstantInt* getUint32(uint32_t value); static ConstantInt* getUint64(uint64_t value); + static ConstantInt* getNative(int64_t value); + static ConstantInt* getUNative(uint64_t value); int64_t getSignedValue(); uint64_t getUnsignedValue(); diff --git a/source/include/ir/function.h b/source/include/ir/function.h index c2843b2d..3ad15d40 100644 --- a/source/include/ir/function.h +++ b/source/include/ir/function.h @@ -1,5 +1,5 @@ // function.h -// Copyright (c) 2014 - 2016, zhiayang@gmail.com +// Copyright (c) 2014 - 2016, zhiayang // Licensed under the Apache License Version 2.0. #pragma once @@ -65,6 +65,14 @@ namespace fir bool isAlwaysInlined(); void setAlwaysInline(); + bool isIntrinsicFunction(); + void setIsIntrinsic(); + + // this is used so the function knows how much space it needs to reserve for + // allocas. + void addStackAllocation(Type* ty); + std::vector getStackAllocations(); + void cullUnusedValues(); // overridden stuff @@ -78,9 +86,11 @@ namespace fir Function(const Identifier& name, FunctionType* fnType, Module* module, LinkageType linkage); std::vector fnArguments; std::vector blocks; + std::vector stackAllocs; bool alwaysInlined = false; bool hadBodyElsewhere = false; + bool fnIsIntrinsicFunction = false; }; } diff --git a/source/include/ir/instruction.h b/source/include/ir/instruction.h index 2f0c4dc2..60a6c54e 100644 --- a/source/include/ir/instruction.h +++ b/source/include/ir/instruction.h @@ -1,5 +1,5 @@ // instruction.h -// Copyright (c) 2014 - 2016, zhiayang@gmail.com +// Copyright (c) 2014 - 2016, zhiayang // Licensed under the Apache License Version 2.0. #pragma once @@ -101,8 +101,6 @@ namespace fir Value_CallFunctionPointer, Value_CallVirtualMethod, Value_Return, - Value_PointerAddition, - Value_PointerSubtraction, Value_GetPointerToStructMember, // equivalent to GEP(ptr*, ptrIndex, memberIndex) -- for structs. Value_GetStructMember, // equivalent to GEP(ptr*, 0, memberIndex) Value_GetPointer, // equivalent to GEP(ptr*, index) diff --git a/source/include/ir/interp.h b/source/include/ir/interp.h new file mode 100644 index 00000000..d987bb4a --- /dev/null +++ b/source/include/ir/interp.h @@ -0,0 +1,128 @@ +// interp.h +// Copyright (c) 2017, zhiayang@gmail.com +// Licensed under the Apache License Version 2.0. + +#pragma once + +#include +#include + +#include +#include +#include + +namespace fir +{ + struct Type; + struct Value; + struct Module; + struct IRBlock; + struct Function; + struct GlobalValue; + struct ConstantValue; + + namespace interp + { + //? is it bad form to name these the same as our fir structs?? + + struct Value + { + fir::Value* val = 0; + + fir::Type* type = 0; + size_t dataSize = 0; + union { + void* ptr; + uint8_t data[32]; + }; + + // kinda a dirty hack, we need to know whether or not to write-back the global after the interpreter + // is done, cases where we modify stuff during compile-time execution. of course, the real value could + // be *derived* from a global, eg. a GEP on a struct, or something. so, we persistently tag it. + fir::GlobalValue* globalValTracker = 0; + }; + + struct Instruction + { + size_t opcode; + fir::Value* result = 0; + std::vector args; + }; + + struct Block + { + fir::IRBlock* blk = 0; + std::vector instructions; + }; + + struct Function + { + fir::Function* func = 0; + bool isExternal = false; + + std::string extFuncName; + interp::Block* entryBlock = 0; + std::vector blocks; + }; + + struct InterpState + { + InterpState(fir::Module* mod); + ~InterpState(); + + void initialise(); + void finalise(); + + interp::Function& compileFunction(fir::Function* fn); + interp::Value runFunction(const interp::Function& fn, const std::vector& args); + + interp::Value makeValue(fir::Value* ty); + + fir::ConstantValue* unwrapInterpValueIntoConstant(const interp::Value& val); + + struct Frame + { + size_t currentInstrIndex = 0; + const interp::Block* currentBlock = 0; + const interp::Block* previousBlock = 0; + const interp::Function* currentFunction = 0; + + std::vector stackAllocs; + + fir::Value* callResultOutput = 0; + + std::unordered_map values; + }; + + // this is the executing state. + std::vector stackFrames; + + std::unordered_map> globals; + std::vector globalAllocs; + + std::vector strings; + + // map from the id to the real function. + // we don't want 'inheritance' here + std::unordered_map compiledFunctions; + + fir::Module* module = 0; + }; + } +} + + + + + + + + + + + + + + + + diff --git a/source/include/ir/irbuilder.h b/source/include/ir/irbuilder.h index 9aa29b91..30fa833b 100644 --- a/source/include/ir/irbuilder.h +++ b/source/include/ir/irbuilder.h @@ -1,5 +1,5 @@ // irbuilder.h -// Copyright (c) 2014 - 2016, zhiayang@gmail.com +// Copyright (c) 2014 - 2016, zhiayang // Licensed under the Apache License Version 2.0. #pragma once @@ -124,11 +124,6 @@ namespace fir Value* BinaryOp(std::string ao, Value* a, Value* b, const std::string& vname = ""); - - Value* PointerAdd(Value* ptr, Value* num, const std::string& vname = ""); - Value* PointerSub(Value* ptr, Value* num, const std::string& vname = ""); - - Value* CreateValue(Type* t, const std::string& vname = ""); Value* ExtractValue(Value* val, const std::vector& inds, const std::string& vname = ""); diff --git a/source/include/ir/module.h b/source/include/ir/module.h index 62cbab79..56e614c1 100644 --- a/source/include/ir/module.h +++ b/source/include/ir/module.h @@ -1,5 +1,5 @@ // module.h -// Copyright (c) 2014 - 2016, zhiayang@gmail.com +// Copyright (c) 2014 - 2016, zhiayang // Licensed under the Apache License Version 2.0. #pragma once @@ -62,6 +62,9 @@ namespace fir Function* getEntryFunction(); void setEntryFunction(Function* fn); + + void finaliseGlobalConstructors(); + const util::hash_map, GlobalVariable*>>& _getVtables() { return this->vtables; } const util::hash_map& _getIntrinsicFunctions() { return this->intrinsicFunctions; } const util::hash_map& _getGlobalStrings() { return this->globalStrings; } @@ -69,8 +72,6 @@ namespace fir const util::hash_map& _getFunctions() { return this->functions; } const util::hash_map& _getNamedTypes() { return this->namedTypes; } - const util::hash_map& _getAllGlobals() { return this->allGlobalValues; } - private: std::string moduleName; @@ -81,8 +82,6 @@ namespace fir util::hash_map functions; util::hash_map namedTypes; - util::hash_map allGlobalValues; - util::hash_map intrinsicFunctions; Function* entryFunction = 0; diff --git a/source/include/ir/type.h b/source/include/ir/type.h index a6185a95..ecbe7ede 100644 --- a/source/include/ir/type.h +++ b/source/include/ir/type.h @@ -1,5 +1,5 @@ // type.h -// Copyright (c) 2014 - 2016, zhiayang@gmail.com +// Copyright (c) 2014 - 2016, zhiayang // Licensed under the Apache License Version 2.0. #pragma once @@ -27,6 +27,7 @@ namespace fir struct UnionType; struct StructType; struct StringType; + struct OpaqueType; struct PointerType; struct FunctionType; struct RawUnionType; @@ -47,6 +48,9 @@ namespace fir int getCastDistance(Type* from, Type* to); bool isRefCountedType(Type* ty); + void setNativeWordSizeInBits(size_t sz); + size_t getNativeWordSizeInBits(); + enum class TypeKind { Invalid, @@ -63,6 +67,7 @@ namespace fir Union, Struct, String, + Opaque, Pointer, Function, RawUnion, @@ -114,6 +119,7 @@ namespace fir RawUnionType* toRawUnionType(); FunctionType* toFunctionType(); PointerType* toPointerType(); + OpaqueType* toOpaqueType(); StructType* toStructType(); StringType* toStringType(); RangeType* toRangeType(); @@ -142,6 +148,8 @@ namespace fir bool isCharType(); bool isStringType(); + bool isOpaqueType(); + bool isAnyType(); bool isEnumType(); bool isArrayType(); @@ -232,6 +240,11 @@ namespace fir static AnyType* getAny(); + static PrimitiveType* getNativeWord(); + static PrimitiveType* getNativeUWord(); + + static PointerType* getNativeWordPtr(); + virtual ~Type() { } const TypeKind kind; @@ -503,7 +516,7 @@ namespace fir // methods size_t getElementCount(); Type* getElementN(size_t n); - std::vector getElements(); + const std::vector& getElements(); virtual std::string str() override; virtual std::string encodedStr() override; @@ -537,7 +550,7 @@ namespace fir size_t getVariantCount(); size_t getIdOfVariant(const std::string& name); - util::hash_map getVariants(); + const util::hash_map& getVariants(); bool hasVariant(const std::string& name); UnionVariantType* getVariant(const std::string& name); @@ -599,7 +612,7 @@ namespace fir bool hasVariant(const std::string& name); Type* getVariant(const std::string& name); - util::hash_map getVariants(); + const util::hash_map& getVariants(); void setBody(const util::hash_map& variants); @@ -634,7 +647,7 @@ namespace fir Type* getElement(const std::string& name); bool hasElementWithName(const std::string& name); size_t getElementIndex(const std::string& name); - std::vector getElements(); + const std::vector& getElements(); void setBody(const std::vector>& members); @@ -680,13 +693,14 @@ namespace fir Type* getElement(const std::string& name); bool hasElementWithName(const std::string& name); size_t getElementIndex(const std::string& name); - std::vector getElements(); + const std::vector& getElements(); + std::vector getAllElementsIncludingBase(); - std::vector getMethods(); + const std::vector& getMethods(); std::vector getMethodsWithName(std::string id); Function* getMethodWithType(FunctionType* ftype); - std::vector getInitialiserFunctions(); + const std::vector& getInitialiserFunctions(); void setInitialiserFunctions(const std::vector& list); Function* getInlineInitialiser(); @@ -905,7 +919,7 @@ namespace fir friend struct Type; // methods - std::vector getArgumentTypes(); + const std::vector& getArgumentTypes(); Type* getArgumentN(size_t n); Type* getReturnType(); @@ -999,6 +1013,29 @@ namespace fir }; + struct OpaqueType : Type + { + friend struct Type; + + + virtual std::string str() override; + virtual std::string encodedStr() override; + virtual bool isTypeEqual(Type* other) override; + virtual fir::Type* substitutePlaceholders(const util::hash_map& subst) override; + + size_t getTypeSizeInBits() { return this->typeSizeInBits; } + + // protected constructor + virtual ~OpaqueType() override { } + protected: + OpaqueType(const std::string& name, size_t sizeInBits); + + std::string typeName; + size_t typeSizeInBits; + + public: + static OpaqueType* get(const std::string& name, size_t sizeInBits); + }; @@ -1008,7 +1045,7 @@ namespace fir struct LocatedType { LocatedType() { } - LocatedType(fir::Type* t) : type(t) { } + explicit LocatedType(fir::Type* t) : type(t) { } LocatedType(fir::Type* t, const Location& l) : type(t), loc(l) { } operator fir::Type* () const { return this->type; } diff --git a/source/include/ir/value.h b/source/include/ir/value.h index 05515f12..81e7faf2 100644 --- a/source/include/ir/value.h +++ b/source/include/ir/value.h @@ -1,5 +1,5 @@ // value.h -// Copyright (c) 2014 - 2016, zhiayang@gmail.com +// Copyright (c) 2014 - 2016, zhiayang // Licensed under the Apache License Version 2.0. #pragma once @@ -72,6 +72,8 @@ namespace fir const Identifier& getName(); bool hasName(); + static size_t getCurrentValueId(); + // protected shit size_t id; protected: diff --git a/source/include/lexer.h b/source/include/lexer.h index 33e43b9c..09fc91a3 100644 --- a/source/include/lexer.h +++ b/source/include/lexer.h @@ -1,5 +1,5 @@ // lexer.h -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #pragma once @@ -122,6 +122,7 @@ namespace lexer UnicodeSymbol, Number, StringLiteral, + CharacterLiteral, NewLine, Comment, EndOfFile, @@ -130,6 +131,10 @@ namespace lexer Attr_EntryFn, Attr_NoMangle, Attr_Operator, + Attr_Platform, + + Directive_Run, + Directive_If, }; struct Token diff --git a/source/include/mpool.h b/source/include/mpool.h index c563e44f..4f77894c 100644 --- a/source/include/mpool.h +++ b/source/include/mpool.h @@ -1,5 +1,5 @@ // mpool.h -// Copyright (c) 2017, zhiayang@gmail.com +// Copyright (c) 2017, zhiayang // Licensed under the Apache License Version 2.0. #pragma once @@ -19,4 +19,4 @@ namespace util addPool(&_pool); return _pool.construct(std::forward(args)...); } -} \ No newline at end of file +} diff --git a/source/include/parser.h b/source/include/parser.h index 7b0a6334..b13e693e 100644 --- a/source/include/parser.h +++ b/source/include/parser.h @@ -1,5 +1,5 @@ // parser.h -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #pragma once diff --git a/source/include/parser_internal.h b/source/include/parser_internal.h index 3a9c7732..283b6f36 100644 --- a/source/include/parser_internal.h +++ b/source/include/parser_internal.h @@ -1,5 +1,5 @@ // parser_internal.h -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #pragma once @@ -232,6 +232,13 @@ namespace parser util::hash_map prefixOps; util::hash_map postfixOps; + // flags that determine whether or not 'import' and '@operator' things can still be done. + bool importsStillValid = true; + bool operatorsStillValid = true; + bool nativeWordSizeStillValid = true; + + frontend::CollectorState* cState = 0; + private: // 1 = inside function // 2 = inside struct @@ -249,7 +256,7 @@ namespace parser pts::Type* parseType(State& st); ast::Expr* parseExpr(State& st); - ast::Stmt* parseStmt(State& st); + ast::Stmt* parseStmt(State& st, bool allowExprs = true); ast::DeferredStmt* parseDefer(State& st); @@ -268,6 +275,11 @@ namespace parser DecompMapping parseArrayDecomp(State& st); DecompMapping parseTupleDecomp(State& st); + std::tuple parseFunctionDecl(State& st); + ast::PlatformDefn* parsePlatformDefn(State& st); + + ast::RunDirective* parseRunDirective(State& st); + ast::EnumDefn* parseEnum(State& st); ast::ClassDefn* parseClass(State& st); ast::StaticDecl* parseStaticDecl(State& st); @@ -289,7 +301,7 @@ namespace parser ast::LitArray* parseArray(State& st, bool israw); ast::Stmt* parseForLoop(State& st); - ast::IfStmt* parseIfStmt(State& st); + ast::Stmt* parseIfStmt(State& st); ast::WhileLoop* parseWhileLoop(State& st); ast::TopLevelBlock* parseTopLevel(State& st, const std::string& name); diff --git a/source/include/platform.h b/source/include/platform.h index e2de28aa..08cf89a5 100644 --- a/source/include/platform.h +++ b/source/include/platform.h @@ -1,5 +1,5 @@ // platform.h -// Copyright (c) 2017, zhiayang@gmail.com +// Copyright (c) 2017, zhiayang // Licensed under the Apache License Version 2.0. // platform-specific things @@ -16,17 +16,12 @@ namespace platform #ifdef _WIN32 - #define WIN32_LEAN_AND_MEAN 1 - - #ifndef NOMINMAX - #define NOMINMAX - #endif - - #include - using filehandle_t = HANDLE; + using filehandle_t = void*; #define CRT_FDOPEN "_fdopen" #define PLATFORM_NEWLINE "\r\n" + + #define PLATFORM_EXPORT_FUNCTION extern "C" __declspec(dllexport) #else #include #include @@ -35,6 +30,7 @@ namespace platform #define CRT_FDOPEN "fdopen" #define PLATFORM_NEWLINE "\n" + #define PLATFORM_EXPORT_FUNCTION extern "C" __attribute__((visibility("default"))) #endif extern filehandle_t InvalidFileHandle; @@ -57,6 +53,24 @@ namespace platform std::string getTemporaryFilename(const std::string& name); size_t getTerminalWidth(); + + void performSelfDlOpen(); + void performSelfDlClose(); + + void* getSymbol(const std::string& name); } + + + + + + + + + + + + + diff --git a/source/include/polymorph.h b/source/include/polymorph.h index 67388c55..ac234ce3 100644 --- a/source/include/polymorph.h +++ b/source/include/polymorph.h @@ -1,5 +1,5 @@ // polymorph.h -// Copyright (c) 2017, zhiayang@gmail.com +// Copyright (c) 2017, zhiayang // Licensed under the Apache License Version 2.0. #pragma once diff --git a/source/include/precompile.h b/source/include/precompile.h index c3d8bd7b..4158fe7d 100644 --- a/source/include/precompile.h +++ b/source/include/precompile.h @@ -1,5 +1,5 @@ // precompile.h -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #pragma once diff --git a/source/include/profiling.h b/source/include/profiling.h deleted file mode 100644 index bb9e723f..00000000 --- a/source/include/profiling.h +++ /dev/null @@ -1,55 +0,0 @@ -// profile.h -// Copyright (c) 2014 - 2016, zhiayang@gmail.com -// Licensed under the Apache License Version 2.0. - -#pragma once - -#include - -#include -#include - -#define PROFGROUP_TOP 0 -#define PROFGROUP_MISC 1 -#define PROFGROUP_LLVM 2 - -namespace prof -{ - using clock = std::chrono::high_resolution_clock; - - struct Profile - { - Profile(std::string name); - Profile(int group, std::string name); - ~Profile(); - - bool operator == (const Profile&) const; - bool operator != (const Profile&) const; - - void finish(); - - std::chrono::time_point begin; - std::string name; - - int group = 0; - bool didRecord = 0; - }; - - void printResults(); -} - - - - - - - - - - - - - - - - diff --git a/source/include/pts.h b/source/include/pts.h index 827895d4..fb0e163a 100644 --- a/source/include/pts.h +++ b/source/include/pts.h @@ -1,5 +1,5 @@ // pts.h -// Copyright (c) 2014 - 2016, zhiayang@gmail.com +// Copyright (c) 2014 - 2016, zhiayang // Licensed under the Apache License Version 2.0. #pragma once @@ -86,7 +86,7 @@ namespace pts static NamedType* create(const Location& l, const std::string& s); static NamedType* create(const Location& l, const std::string& s, const PolyArgMapping_t& genericMapping); - private: + // private: explicit NamedType(const Location& l, const std::string& n) : Type(l), name(n) { } }; diff --git a/source/include/resolver.h b/source/include/resolver.h index dce02245..8bc52872 100644 --- a/source/include/resolver.h +++ b/source/include/resolver.h @@ -1,5 +1,5 @@ // resolver.h -// Copyright (c) 2017, zhiayang@gmail.com +// Copyright (c) 2017, zhiayang // Licensed under the Apache License Version 2.0. #pragma once @@ -38,7 +38,7 @@ namespace sst std::pair, ErrorMsg*> verifyStructConstructorArguments(const Location& callLoc, - const std::string& name, const std::set& fieldNames, const std::vector& arguments); + const std::string& name, const std::vector& fieldNames, const std::vector& arguments); TCResult resolveAndInstantiatePolymorphicUnion(TypecheckState* fs, sst::UnionVariantDefn* uvd, std::vector* arguments, fir::Type* return_infer, bool isFnCall); diff --git a/source/include/sst.h b/source/include/sst.h index fc0998b9..ca65725a 100644 --- a/source/include/sst.h +++ b/source/include/sst.h @@ -1,5 +1,5 @@ // sst.h -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #pragma once @@ -511,6 +511,16 @@ namespace sst bool value = false; }; + struct LiteralChar : Expr + { + LiteralChar(const Location& l, fir::Type* t) : Expr(l, t) { this->readableName = "character literal"; } + ~LiteralChar() { } + + virtual CGResult _codegen(cgn::CodegenState* cs, fir::Type* inferred = 0) override; + + uint32_t value = false; + }; + struct LiteralTuple : Expr { LiteralTuple(const Location& l, fir::Type* t) : Expr(l, t) { this->readableName = "tuple literal"; } @@ -648,6 +658,7 @@ namespace sst virtual CGResult _codegen(cgn::CodegenState* cs, fir::Type* inferred = 0) override; + bool isIntrinsic = false; std::string realName; }; @@ -695,6 +706,15 @@ namespace sst }; + struct BareTypeDefn : TypeDefn + { + BareTypeDefn(const Location& l) : TypeDefn(l) { this->readableName = "type definition"; } + ~BareTypeDefn() { } + + virtual std::string getKind() override { return "type"; } + virtual CGResult _codegen(cgn::CodegenState* cs, fir::Type* inferred = 0) override; + }; + struct StructDefn : TypeDefn { @@ -806,6 +826,17 @@ namespace sst std::vector args; }; + struct RunDirective : Expr + { + RunDirective(const Location& l, fir::Type* t) : Expr(l, t) { this->readableName = "run directive"; } + ~RunDirective() { } + + virtual CGResult _codegen(cgn::CodegenState* cs, fir::Type* inferred = 0) override; + + // mutually exclusive! + Block* block = 0; + Expr* insideExpr = 0; + }; } diff --git a/source/include/sst_expr.h b/source/include/sst_expr.h index 6ed00b8a..130c1b4a 100644 --- a/source/include/sst_expr.h +++ b/source/include/sst_expr.h @@ -1,5 +1,5 @@ // sst_expr.h -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #pragma once @@ -18,21 +18,10 @@ namespace sst Stmt(const Location& l) : Locatable(l, "statement") { } virtual ~Stmt() { } - virtual CGResult codegen(cgn::CodegenState* cs, fir::Type* inferred = 0) - { - if(didCodegen) - { - return cachedResult; - } - else - { - this->didCodegen = true; - return (this->cachedResult = this->_codegen(cs, inferred)); - } - } - + virtual CGResult codegen(cgn::CodegenState* cs, fir::Type* inferred = 0); virtual CGResult _codegen(cgn::CodegenState* cs, fir::Type* inferred = 0) = 0; + size_t cachedCSId = 0; bool didCodegen = false; CGResult cachedResult = CGResult(0); }; @@ -53,6 +42,7 @@ namespace sst Identifier id; fir::Type* type = 0; bool global = false; + std::string bareName; VisibilityLevel visibility = VisibilityLevel::Internal; virtual std::string getKind() = 0; diff --git a/source/include/stcommon.h b/source/include/stcommon.h index 9cdfc256..7317cae2 100644 --- a/source/include/stcommon.h +++ b/source/include/stcommon.h @@ -1,5 +1,5 @@ // stcommon.h -// Copyright (c) 2017, zhiayang@gmail.com +// Copyright (c) 2017, zhiayang // Licensed under the Apache License Version 2.0. #pragma once diff --git a/source/include/typecheck.h b/source/include/typecheck.h index 2fad6173..c51d2284 100644 --- a/source/include/typecheck.h +++ b/source/include/typecheck.h @@ -1,5 +1,5 @@ // typecheck.h -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #pragma once @@ -115,7 +115,7 @@ namespace sst std::string moduleName; DefinitionTree* dtree = 0; - StateTree*& stree; + StateTree* stree = 0; util::hash_map typeDefnMap; @@ -207,7 +207,7 @@ namespace sst }; DefinitionTree* typecheck(frontend::CollectorState* cs, const parser::ParsedFile& file, - const std::vector>& imports); + const std::vector>& imports, bool addPreludeDefinitions); StateTree* addTreeToExistingTree(StateTree* to, StateTree* from, StateTree* commonParent, bool pubImport, bool ignoreVis); diff --git a/source/include/utils.h b/source/include/utils.h index 1e6a4bd2..d5474a3b 100644 --- a/source/include/utils.h +++ b/source/include/utils.h @@ -1,5 +1,5 @@ // utils.h -// Copyright (c) 2017, zhiayang@gmail.com +// Copyright (c) 2017, zhiayang // Licensed under the Apache License Version 2.0. #pragma once diff --git a/source/main.cpp b/source/main.cpp index 542b9772..ebf1ad85 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -1,5 +1,5 @@ // main.cpp -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "defs.h" @@ -12,6 +12,9 @@ #include "mpool.h" #include "allocator.h" +#include "ir/interp.h" + + struct timer { @@ -24,25 +27,6 @@ struct timer }; -#ifdef _WIN32 -#define DLLEXPORT __declspec(dllexport) -#else -#define DLLEXPORT -#endif - -extern "C" DLLEXPORT void lmao() -{ - printf("LMAO!\n"); -} - -extern "C" DLLEXPORT void haha(int x) -{ - for(int i = 0; i < x; i++) - printf("HA"); - - printf("!\n"); -} - static void compile(std::string in, std::string out) { @@ -60,27 +44,37 @@ static void compile(std::string in, std::string out) { timer t(&lexer_ms); frontend::collectFiles(in, &state); - // debuglogln("lexed (%.2f) - (a: %.2fk, f: %.2fk, w: %.2fk)", total.stop(), - // mem::getAllocatedCount() / 1024.0, mem::getDeallocatedCount() / 1024.0, mem::getWatermark() / 1024.0); - // mem::resetStats(); + + if(frontend::getPrintProfileStats()) + { + debuglogln("lex (%.1f ms)\t[w: %.1fk, f: %.1fk, a: %.1fk]", total.stop(), mem::getWatermark() / 1024.0, + mem::getDeallocatedCount() / 1024.0, mem::getAllocatedCount() / 1024.0); + // mem::resetStats(); + } } { timer t(&parser_ms); frontend::parseFiles(&state); - // debuglogln("parsed (%.2f) - (a: %.2fk, f: %.2fk, w: %.2fk)", total.stop(), - // mem::getAllocatedCount() / 1024.0, mem::getDeallocatedCount() / 1024.0, mem::getWatermark() / 1024.0); - // mem::resetStats(); + + if(frontend::getPrintProfileStats()) + { + debuglogln("parse (%.1f ms)\t[w: %.1fk, f: %.1fk, a: %.1fk]", total.stop(), mem::getWatermark() / 1024.0, + mem::getDeallocatedCount() / 1024.0, mem::getAllocatedCount() / 1024.0); + // mem::resetStats(); + } } { timer t(&typecheck_ms); - dtree = frontend::typecheckFiles(&state); - // debuglogln("typechecked (%.2f) - (a: %.2fk, f: %.2fk, w: %.2fk)", total.stop(), - // mem::getAllocatedCount() / 1024.0, mem::getDeallocatedCount() / 1024.0, mem::getWatermark() / 1024.0); - // mem::resetStats(); + if(frontend::getPrintProfileStats()) + { + debuglogln("typechk (%.1f ms)\t[w: %.1fk, f: %.1fk, a: %.1fk]", total.stop(), mem::getWatermark() / 1024.0, + mem::getDeallocatedCount() / 1024.0, mem::getAllocatedCount() / 1024.0); + // mem::resetStats(); + } iceAssert(dtree); } @@ -88,29 +82,39 @@ static void compile(std::string in, std::string out) timer t(nullptr); + platform::performSelfDlOpen(); + fir::Module* module = frontend::generateFIRModule(&state, dtree); + module->finaliseGlobalConstructors(); + + auto cd = backend::CompiledData { module }; - // debuglogln("codegened (%.2f) - (a: %.2fk, f: %.2fk, w: %.2fk)", total.stop(), - // mem::getAllocatedCount() / 1024.0, mem::getDeallocatedCount() / 1024.0, mem::getWatermark() / 1024.0); + if(frontend::getPrintProfileStats()) + { + debuglogln("codegen (%.1f ms)\t[w: %.1fk, f: %.1fk, a: %.1fk]", total.stop(), mem::getWatermark() / 1024.0, + mem::getDeallocatedCount() / 1024.0, mem::getAllocatedCount() / 1024.0); + } auto codegen_ms = t.stop(); - auto compile_ms = (double) (std::chrono::high_resolution_clock::now() - ts).count() / 1000.0 / 1000.0; - printf("compile took %.1f (lexer: %.1f, parser: %.1f, typecheck: %.1f, codegen: %.1f) ms%s\n", - compile_ms, lexer_ms, parser_ms, typecheck_ms, codegen_ms, - compile_ms > 3000 ? strprintf(" (aka %.2f s)", compile_ms / 1000.0).c_str() : ""); + // delete all the memory we've allocated. + util::clearAllPools(); + + if(frontend::getPrintProfileStats()) + { + auto compile_ms = (double) (std::chrono::high_resolution_clock::now() - ts).count() / 1000.0 / 1000.0; + + debuglogln("cleared (%.1f ms)\t[w: %.1fk, f: %.1fk, a: %.1fk]", total.stop(), mem::getWatermark() / 1024.0, + mem::getDeallocatedCount() / 1024.0, mem::getAllocatedCount() / 1024.0); + debuglogln("compile (%.1f ms)\t[l: %.1f, p: %.1f, t: %.1f, c: %.1f]", compile_ms, lexer_ms, parser_ms, typecheck_ms, codegen_ms); + debuglogln("%zu FIR values generated\n", fir::Value::getCurrentValueId()); + } - printf("%zu FIR values generated\n", fir::ConstantBool::get(false)->id); if(frontend::getPrintFIR()) fprintf(stderr, "%s\n", module->print().c_str()); - // delete all the memory we've allocated. - util::clearAllPools(); - // debuglogln("cleared (%.2f) - (a: %.2fk, f: %.2fk, w: %.2fk)", total.stop(), - // mem::getAllocatedCount() / 1024.0, mem::getDeallocatedCount() / 1024.0, mem::getWatermark() / 1024.0); - { using namespace backend; Backend* backend = Backend::getBackendFromOption(frontend::getBackendOption(), cd, { in }, out); @@ -130,14 +134,13 @@ static void compile(std::string in, std::string out) if(backend->hasCapability((BackendCaps::Capabilities) capsneeded)) { - // auto p = prof::Profile(PROFGROUP_LLVM, "llvm_total"); backend->performCompilation(); backend->optimiseProgram(); backend->writeOutput(); } else { - error("Selected backend '%s' does not have some required capabilities (missing '%s')\n", backend->str(), + error("selected backend '%s' does not have some required capabilities (missing %s)\n", backend->str(), capabilitiesToString((BackendCaps::Capabilities) capsneeded)); } } diff --git a/source/misc/allocator.cpp b/source/misc/allocator.cpp index 45505e2d..6d051a33 100644 --- a/source/misc/allocator.cpp +++ b/source/misc/allocator.cpp @@ -1,5 +1,5 @@ // allocator.cpp -// Copyright (c) 2017, zhiayang@gmail.com +// Copyright (c) 2017, zhiayang // Licensed under the Apache License Version 2.0. #include @@ -21,13 +21,13 @@ namespace mem { #ifdef _WIN32 auto ret = VirtualAlloc(nullptr, bytes, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); - if(ret == nullptr) _error_and_exit("failed to allocate %d bytes of memory (large page min: %d)", bytes, GetLargePageMinimum()); + if(ret == nullptr) _error_and_exit("failed to allocate %d bytes of memory (large page min: %d)\n", bytes, GetLargePageMinimum()); return ret; #else auto ret = mmap(nullptr, bytes, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if(ret == nullptr || (uintptr_t) ret == (uintptr_t) -1) - _error_and_exit("failed to allocate %d bytes of memory", bytes); + _error_and_exit("failed to allocate %d bytes of memory\n", bytes); return ret; #endif diff --git a/source/misc/destructors.cpp b/source/misc/destructors.cpp index 58e3499d..ebca699c 100644 --- a/source/misc/destructors.cpp +++ b/source/misc/destructors.cpp @@ -1,5 +1,5 @@ // destructors.cpp -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "ast.h" diff --git a/source/misc/identifier.cpp b/source/misc/identifier.cpp index d2dfd8eb..cef55b60 100644 --- a/source/misc/identifier.cpp +++ b/source/misc/identifier.cpp @@ -1,5 +1,5 @@ // identifier.cpp -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "defs.h" @@ -17,7 +17,7 @@ sst::Stmt* TCResult::stmt() const case RK::Statement: return this->_st; case RK::Expression: return this->_ex; case RK::Definition: return this->_df; - default: _error_and_exit("not stmt"); + default: _error_and_exit("not stmt\n"); } } @@ -69,6 +69,39 @@ void PolyArgMapping_t::add(size_t idx, pts::Type* t) +std::string util::obfuscateName(const std::string& name) +{ + return strprintf("__#%s", name); +} +std::string util::obfuscateName(const std::string& name, size_t id) +{ + return strprintf("__#%s_%zu", name, id); +} +std::string util::obfuscateName(const std::string& name, const std::string& extra) +{ + return strprintf("__#%s_%s", name, extra); +} +Identifier util::obfuscateIdentifier(const std::string& name, IdKind kind) +{ + return Identifier(obfuscateName(name), kind); +} +Identifier util::obfuscateIdentifier(const std::string& name, size_t id, IdKind kind) +{ + return Identifier(obfuscateName(name, id), kind); +} +Identifier util::obfuscateIdentifier(const std::string& name, const std::string& extra, IdKind kind) +{ + return Identifier(obfuscateName(name, extra), kind); +} + + + + + + + + + bool Identifier::operator == (const Identifier& other) const { @@ -205,7 +238,7 @@ static std::string mangleType(fir::Type* t) } else { - _error_and_exit("unsupported ir type??? ('%s')", t); + _error_and_exit("unsupported ir type??? ('%s')\n", t); } } @@ -245,7 +278,7 @@ static std::string _doMangle(const Identifier& id, bool includeScope) } else { - _error_and_exit("invalid"); + _error_and_exit("invalid\n"); } } else diff --git a/source/misc/mpool.cpp b/source/misc/mpool.cpp index dbc44545..0f1aa2b3 100644 --- a/source/misc/mpool.cpp +++ b/source/misc/mpool.cpp @@ -1,5 +1,5 @@ // mpool.cpp -// Copyright (c) 2017, zhiayang@gmail.com +// Copyright (c) 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "defs.h" @@ -20,4 +20,4 @@ namespace util for(auto pool : pools) pool->clear(); } -} \ No newline at end of file +} diff --git a/source/typecheck/alloc.cpp b/source/typecheck/alloc.cpp index 6ee206e3..15bda19b 100644 --- a/source/typecheck/alloc.cpp +++ b/source/typecheck/alloc.cpp @@ -1,5 +1,5 @@ // alloc.cpp -// Copyright (c) 2017, zhiayang@gmail.com +// Copyright (c) 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "ast.h" @@ -24,7 +24,7 @@ TCResult ast::AllocOp::typecheck(sst::TypecheckState* fs, fir::Type* infer) error(this, "only one length dimension is supported for raw memory allocation (have %d)", this->counts.size()); std::vector counts = util::map(this->counts, [fs](ast::Expr* e) -> auto { - auto c = e->typecheck(fs, fir::Type::getInt64()).expr(); + auto c = e->typecheck(fs, fir::Type::getNativeWord()).expr(); if(!c->type->isIntegerType()) error(c, "expected integer type ('i64') for alloc count, found '%s' instead", c->type); @@ -76,7 +76,7 @@ TCResult ast::AllocOp::typecheck(sst::TypecheckState* fs, fir::Type* infer) fake->name = "it"; auto fake2 = util::pool(this->initBody->loc); - fake2->type = pts::NamedType::create(this->initBody->loc, INT64_TYPE_STRING); + fake2->type = pts::NamedType::create(this->initBody->loc, INTUNSPEC_TYPE_STRING); fake2->name = "i"; // make a temp scope to enclose it, I guess diff --git a/source/typecheck/arithmetic.cpp b/source/typecheck/arithmetic.cpp index 1fa60215..d653bcb1 100644 --- a/source/typecheck/arithmetic.cpp +++ b/source/typecheck/arithmetic.cpp @@ -1,5 +1,5 @@ // arithmetic.cpp -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "ast.h" diff --git a/source/typecheck/assign.cpp b/source/typecheck/assign.cpp index ee91bf8e..3372fdb2 100644 --- a/source/typecheck/assign.cpp +++ b/source/typecheck/assign.cpp @@ -1,5 +1,5 @@ // assign.cpp -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "ast.h" diff --git a/source/typecheck/call.cpp b/source/typecheck/call.cpp index 9b665378..d78b6a26 100644 --- a/source/typecheck/call.cpp +++ b/source/typecheck/call.cpp @@ -1,5 +1,5 @@ // call.cpp -// Copyright (c) 2017, zhiayang@gmail.com +// Copyright (c) 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "ast.h" diff --git a/source/typecheck/classes.cpp b/source/typecheck/classes.cpp index e712032a..bcb7d6ea 100644 --- a/source/typecheck/classes.cpp +++ b/source/typecheck/classes.cpp @@ -1,5 +1,5 @@ // classes.cpp -// Copyright (c) 2017, zhiayang@gmail.com +// Copyright (c) 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "ast.h" @@ -32,6 +32,8 @@ TCResult ast::ClassDefn::generateDeclaration(sst::TypecheckState* fs, fir::Type* auto defnname = util::typeParamMapToString(this->name, gmaps); auto defn = util::pool(this->loc); + defn->bareName = this->name; + defn->id = Identifier(defnname, IdKind::Type); defn->id.scope = this->realScope; defn->visibility = this->visibility; @@ -220,7 +222,7 @@ TCResult ast::ClassDefn::typecheck(sst::TypecheckState* fs, fir::Type* infer, co err->append( SimpleError::make(MsgType::Note, bf->loc, "'%s' was previously defined in the base class as a non-virtual method here:", bf->id.name)->append( - BareError::make(MsgType::Note, "To override it, define '%s' as a virtual method", bf->id.name) + BareError::make(MsgType::Note, "to override it, define '%s' as a virtual method", bf->id.name) ) ); } @@ -278,7 +280,7 @@ TCResult ast::ClassDefn::typecheck(sst::TypecheckState* fs, fir::Type* infer, co for(auto sub : from->subtrees) { if(to->subtrees.find(sub.first) == to->subtrees.end()) - to->subtrees[sub.first] = new sst::StateTree(sub.first, sub.second->topLevelFilename, to); + to->subtrees[sub.first] = util::pool(sub.first, sub.second->topLevelFilename, to); recursivelyImport(sub.second, to->subtrees[sub.first]); } diff --git a/source/typecheck/controlflow.cpp b/source/typecheck/controlflow.cpp index e54c9288..2e46077e 100644 --- a/source/typecheck/controlflow.cpp +++ b/source/typecheck/controlflow.cpp @@ -1,5 +1,5 @@ // controlflow.cpp -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "ast.h" @@ -23,6 +23,9 @@ TCResult ast::IfStmt::typecheck(sst::TypecheckState* fs, fir::Type* infer) for(auto c : this->cases) { + //* here, it is implicit that all the inits of every case live in the same scope. + //? we might want to change this eventually? i'm not sure. + auto inits = util::map(c.inits, [fs](Stmt* s) -> auto { return s->typecheck(fs).stmt(); }); auto cs = Case(c.cond->typecheck(fs).expr(), dcast(sst::Block, c.body->typecheck(fs).stmt()), inits); diff --git a/source/typecheck/defer.cpp b/source/typecheck/defer.cpp index 43cb9320..56cc116f 100644 --- a/source/typecheck/defer.cpp +++ b/source/typecheck/defer.cpp @@ -1,5 +1,5 @@ // defer.cpp -// Copyright (c) 2017, zhiayang@gmail.com +// Copyright (c) 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "ast.h" diff --git a/source/typecheck/destructure.cpp b/source/typecheck/destructure.cpp index 9a10e6a6..d883b94d 100644 --- a/source/typecheck/destructure.cpp +++ b/source/typecheck/destructure.cpp @@ -1,5 +1,5 @@ // destructure.cpp -// Copyright (c) 2017, zhiayang@gmail.com +// Copyright (c) 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "ast.h" diff --git a/source/typecheck/directives.cpp b/source/typecheck/directives.cpp new file mode 100644 index 00000000..e49462ed --- /dev/null +++ b/source/typecheck/directives.cpp @@ -0,0 +1,142 @@ +// directives.cpp +// Copyright (c) 2019, zhiayang +// Licensed under the Apache License Version 2.0. + +#include "ast.h" +#include "pts.h" +#include "errors.h" +#include "resolver.h" + +#include "codegen.h" +#include "typecheck.h" + +#include "ir/type.h" +#include "ir/interp.h" + +#include "mpool.h" + + +TCResult ast::RunDirective::typecheck(sst::TypecheckState* fs, fir::Type* infer) +{ + fs->pushLoc(this); + defer(fs->popLoc()); + + auto rundir = util::pool(this->loc, nullptr); + if(this->insideExpr) + { + rundir->insideExpr = this->insideExpr->typecheck(fs, infer).expr(); + rundir->type = rundir->insideExpr->type; + } + else + { + rundir->block = dcast(sst::Block, this->block->typecheck(fs, infer).stmt()); + iceAssert(rundir->block); + + rundir->type = fir::Type::getVoid(); + } + + return TCResult(rundir); +} + + + + +// defined in codegen/directives.cpp +fir::ConstantValue* magicallyRunExpressionAtCompileTime(cgn::CodegenState* cs, sst::Stmt* stmt, fir::Type* infer, const Identifier& fname); + +static size_t condCounter = 0; +TCResult ast::IfDirective::typecheck(sst::TypecheckState* fs, fir::Type* infer) +{ + fs->pushLoc(this); + defer(fs->popLoc()); + + // the entire point of #if is that when the condition is false, we don't typecheck it at all. + // (of course, it must still parse.) in order to achieve this, together with arbitrary code in the + // conditions, we first need to codegen anything that might have been seen -- eg. globals, functions, etc, + // so that we can run the interpreter. + + // thus, we need to *run codegen* *right now*, to get a module. we don't call cgn::codegen directly, but we setup the + // internals ourselves, since cgn::codegen expects a fully typechecked module/dtree, which we don't have right now + auto mod = new fir::Module(""); + auto cs = new cgn::CodegenState(fir::IRBuilder(mod)); + cs->typeDefnMap = fs->typeDefnMap; + cs->module = mod; + + // so we don't crash, give us a starting location. + cs->pushLoc(this->loc); + + defer(delete cs); + defer(delete mod); + + sst::Block* execBlock = 0; + for(auto c : this->cases) + { + if(!c.inits.empty()) + error(c.inits[0], "compile-time #if currently does not support initialisers"); + + auto cond = c.cond->typecheck(fs, fir::Type::getBool()).expr(); + auto value = magicallyRunExpressionAtCompileTime(cs, cond, fir::Type::getBool(), + util::obfuscateIdentifier("interp_if_cond", condCounter++)); + + if(!value->getType()->isBoolType()) + error(c.cond, "expression with non-boolean type '%s' cannot be used as a conditional in an #if", value->getType()); + + auto b = dcast(fir::ConstantBool, value); + iceAssert(b); + + if(b->getValue() == true) // be a bit explicit + { + execBlock = dcast(sst::Block, c.body->typecheck(fs).stmt()); + iceAssert(execBlock); + break; + } + } + + if(!execBlock && this->elseCase) + { + execBlock = dcast(sst::Block, this->elseCase->typecheck(fs).stmt()); + iceAssert(execBlock); + } + + if(!execBlock) + { + // ok we need to return a fake value. + auto ret = util::pool(this->loc, fir::Type::getVoid()); + return TCResult(ret); + } + else + { + return TCResult(execBlock); + } +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/source/typecheck/dotop.cpp b/source/typecheck/dotop.cpp index 5664e4fd..385417ff 100644 --- a/source/typecheck/dotop.cpp +++ b/source/typecheck/dotop.cpp @@ -1,5 +1,5 @@ // dotop.cpp -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "ast.h" @@ -92,7 +92,7 @@ static std::vector searchTransparentFields(sst::TypecheckState* if(df->isTransparentField) { auto ty = df->type; - assert(ty->isRawUnionType() || ty->isStructType()); + iceAssert(ty->isRawUnionType() || ty->isStructType()); auto defn = fs->typeDefnMap[ty]; iceAssert(defn); @@ -241,7 +241,7 @@ static sst::Expr* doExpressionDotOp(sst::TypecheckState* fs, ast::DotOperator* d // TODO: Extension support here fir::Type* res = 0; if(util::match(vr->name, BUILTIN_SAA_FIELD_LENGTH, BUILTIN_SAA_FIELD_CAPACITY, BUILTIN_SAA_FIELD_REFCOUNT, BUILTIN_STRING_FIELD_COUNT)) - res = fir::Type::getInt64(); + res = fir::Type::getNativeWord(); else if(vr->name == BUILTIN_SAA_FIELD_POINTER) res = fir::Type::getInt8Ptr(); @@ -306,7 +306,7 @@ static sst::Expr* doExpressionDotOp(sst::TypecheckState* fs, ast::DotOperator* d if(vr->name == BUILTIN_SAA_FIELD_LENGTH || (type->isDynamicArrayType() && util::match(vr->name, BUILTIN_SAA_FIELD_CAPACITY, BUILTIN_SAA_FIELD_REFCOUNT))) { - res = fir::Type::getInt64(); + res = fir::Type::getNativeWord(); } else if(vr->name == BUILTIN_SAA_FIELD_POINTER) { @@ -390,7 +390,7 @@ static sst::Expr* doExpressionDotOp(sst::TypecheckState* fs, ast::DotOperator* d res = fir::Type::getString(); else if(vr->name == BUILTIN_ENUM_FIELD_INDEX) - res = fir::Type::getInt64(); + res = fir::Type::getNativeWord(); else if(vr->name == BUILTIN_ENUM_FIELD_VALUE) res = type->toEnumType()->getCaseType(); @@ -415,7 +415,7 @@ static sst::Expr* doExpressionDotOp(sst::TypecheckState* fs, ast::DotOperator* d // TODO: extension support here fir::Type* res = 0; if(vr->name == BUILTIN_RANGE_FIELD_BEGIN || vr->name == BUILTIN_RANGE_FIELD_END || vr->name == BUILTIN_RANGE_FIELD_STEP) - res = fir::Type::getInt64(); + res = fir::Type::getNativeWord(); if(res) { @@ -437,10 +437,10 @@ static sst::Expr* doExpressionDotOp(sst::TypecheckState* fs, ast::DotOperator* d // TODO: extension support here fir::Type* res = 0; if(vr->name == BUILTIN_ANY_FIELD_TYPEID) - res = fir::Type::getUint64(); + res = fir::Type::getNativeUWord(); else if(vr->name == BUILTIN_ANY_FIELD_REFCOUNT) - res = fir::Type::getInt64(); + res = fir::Type::getNativeWord(); if(res) { @@ -459,7 +459,6 @@ static sst::Expr* doExpressionDotOp(sst::TypecheckState* fs, ast::DotOperator* d // ok. auto defn = fs->typeDefnMap[type]; - iceAssert(defn); if(auto str = dcast(sst::StructDefn, defn)) { @@ -689,6 +688,8 @@ static sst::Expr* doExpressionDotOp(sst::TypecheckState* fs, ast::DotOperator* d } else { + // TODO: this error message could be better!!! + //* it's because we are pending extension support! error(lhs, "unsupported left-side expression (with type '%s') for dot-operator", lhs->type); } @@ -814,6 +815,8 @@ static sst::Expr* doStaticDotOp(sst::TypecheckState* fs, ast::DotOperator* dot, defer(fs->popGenericContext()); { int pses = sst::poly::internal::getNextSessionId(); + + iceAssert(typdef->original); for(auto g : typdef->original->generics) fs->addGenericMapping(g.first, fir::PolyPlaceholderType::get(g.first, pses)); } @@ -854,7 +857,7 @@ static sst::Expr* doStaticDotOp(sst::TypecheckState* fs, ast::DotOperator* dot, "could not infer type parameters for polymorphic union '%s' using variant '%s' ", unn->id.name, name)->append(SimpleError::make(MsgType::Note, unn->variants[name]->loc, "variant was defined here:"))->postAndQuit(); } - else if(wasfncall && unn->type->toUnionType()->getVariants()[name]->getInteriorType()->isVoidType()) + else if(wasfncall && unn->type->toUnionType()->getVariants().at(name)->getInteriorType()->isVoidType()) { SimpleError::make(dot->right->loc, "variant '%s' of union does not have values, and cannot be constructed via function-call", diff --git a/source/typecheck/enums.cpp b/source/typecheck/enums.cpp index 486b9915..db860931 100644 --- a/source/typecheck/enums.cpp +++ b/source/typecheck/enums.cpp @@ -1,5 +1,5 @@ // enums.cpp -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "ast.h" @@ -37,6 +37,8 @@ TCResult ast::EnumDefn::generateDeclaration(sst::TypecheckState* fs, fir::Type* auto defnname = util::typeParamMapToString(this->name, gmaps); auto defn = util::pool(this->loc); + defn->bareName = this->name; + defn->id = Identifier(defnname, IdKind::Type); defn->id.scope = this->realScope; defn->visibility = this->visibility; @@ -69,7 +71,7 @@ TCResult ast::EnumDefn::typecheck(sst::TypecheckState* fs, fir::Type* infer, con fs->pushTree(defn->id.name); if(this->memberType) defn->memberType = fs->convertParserTypeToFIR(this->memberType); - else defn->memberType = fir::Type::getInt64(); + else defn->memberType = fir::Type::getNativeWord(); auto ety = fir::EnumType::get(defn->id, defn->memberType); diff --git a/source/typecheck/function.cpp b/source/typecheck/function.cpp index 4fd675ed..8a7ff512 100644 --- a/source/typecheck/function.cpp +++ b/source/typecheck/function.cpp @@ -1,5 +1,5 @@ // function.cpp -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "ast.h" @@ -57,6 +57,7 @@ TCResult ast::FuncDefn::generateDeclaration(sst::TypecheckState* fs, fir::Type* defn->parentTypeForMethod = infer; + defn->bareName = this->name; defn->id = Identifier(this->name, IdKind::Function); defn->id.scope = this->realScope; defn->id.params = ptys; @@ -195,6 +196,7 @@ TCResult ast::ForeignFuncDefn::typecheck(sst::TypecheckState* fs, fir::Type* inf auto retty = fs->convertParserTypeToFIR(this->returnType); defn->id = Identifier(this->name, IdKind::Name); + defn->bareName = this->name; defn->params = ps; defn->returnType = retty; @@ -203,6 +205,7 @@ TCResult ast::ForeignFuncDefn::typecheck(sst::TypecheckState* fs, fir::Type* inf // the realname is the actual name of the function. defn->realName = this->realName; + defn->isIntrinsic = this->isIntrinsic; if(this->isVarArg) defn->type = fir::FunctionType::getCVariadicFunc(util::map(ps, [](FnParam p) -> auto { return p.type; }), retty); @@ -252,10 +255,10 @@ TCResult ast::Block::typecheck(sst::TypecheckState* fs, fir::Type* inferred) fs->pushLoc(this); defer(fs->popLoc()); - if(!this->isFunctionBody) + if(!this->isFunctionBody && !this->doNotPushNewScope) fs->pushAnonymousTree(); - defer(!this->isFunctionBody ? fs->popTree() : (sst::StateTree*) nullptr); + defer((!this->isFunctionBody && !this->doNotPushNewScope) ? fs->popTree() : (sst::StateTree*) nullptr); auto ret = util::pool(this->loc); @@ -274,11 +277,7 @@ TCResult ast::Block::typecheck(sst::TypecheckState* fs, fir::Type* inferred) { if(inferred->isVoidType()) { - // no issues. - auto rst = util::pool(s->loc); - rst->expectedType = fir::Type::getVoid(); - - ret->statements = { ex, rst }; + ret->statements = { ex }; } else { @@ -287,11 +286,18 @@ TCResult ast::Block::typecheck(sst::TypecheckState* fs, fir::Type* inferred) } else { - auto rst = util::pool(s->loc); - rst->expectedType = (inferred ? inferred : fs->getCurrentFunction()->returnType); - rst->value = ex; + if(!fs->getCurrentFunction()->returnType->isVoidType()) + { + auto rst = util::pool(s->loc); + rst->expectedType = (inferred ? inferred : fs->getCurrentFunction()->returnType); + rst->value = ex; - ret->statements = { rst }; + ret->statements = { rst }; + } + else + { + ret->statements = { ex }; + } } } else diff --git a/source/typecheck/literals.cpp b/source/typecheck/literals.cpp index f0448597..7c589376 100644 --- a/source/typecheck/literals.cpp +++ b/source/typecheck/literals.cpp @@ -1,5 +1,5 @@ // literals.cpp -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "ast.h" @@ -22,7 +22,7 @@ TCResult ast::LitNumber::typecheck(sst::TypecheckState* fs, fir::Type* infer) auto number = mpfr::mpreal(this->num, mpfr_get_default_prec(), base); bool sgn = mpfr::signbit(number); - bool flt = !mpfr::isint(number); + bool flt = ((this->num.find(".") != std::string::npos) || !mpfr::isint(number)); size_t bits = 0; if(flt) @@ -50,10 +50,12 @@ TCResult ast::LitNumber::typecheck(sst::TypecheckState* fs, fir::Type* infer) bits = sizeof(uintmax_t) * CHAR_BIT; else // lmao - bits = SIZE_MAX; + bits = sizeof(uintmax_t) * CHAR_BIT; } - auto ret = util::pool(this->loc, (infer && infer->isPrimitiveType()) ? infer : fir::ConstantNumberType::get(sgn, flt, bits)); + auto ret = util::pool(this->loc, (infer && infer->isPrimitiveType()) ? infer + : fir::ConstantNumberType::get(sgn, flt, bits)); + ret->num = number; return TCResult(ret); @@ -79,6 +81,17 @@ TCResult ast::LitBool::typecheck(sst::TypecheckState* fs, fir::Type* infer) return TCResult(ret); } +TCResult ast::LitChar::typecheck(sst::TypecheckState* fs, fir::Type* infer) +{ + fs->pushLoc(this); + defer(fs->popLoc()); + + auto ret = util::pool(this->loc, fir::Type::getInt8()); + ret->value = this->value; + + return TCResult(ret); +} + TCResult ast::LitTuple::typecheck(sst::TypecheckState* fs, fir::Type* infer) { fs->pushLoc(this); diff --git a/source/typecheck/loops.cpp b/source/typecheck/loops.cpp index 3e498c07..562a27c2 100644 --- a/source/typecheck/loops.cpp +++ b/source/typecheck/loops.cpp @@ -1,5 +1,5 @@ // loops.cpp -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "pts.h" @@ -29,7 +29,7 @@ TCResult ast::ForeachLoop::typecheck(sst::TypecheckState* fs, fir::Type* inferre elmty = ret->array->type->getArrayElementType(); else if(ret->array->type->isRangeType()) - elmty = fir::Type::getInt64(); + elmty = fir::Type::getNativeWord(); else if(ret->array->type->isStringType()) elmty = fir::Type::getInt8(); @@ -44,7 +44,7 @@ TCResult ast::ForeachLoop::typecheck(sst::TypecheckState* fs, fir::Type* inferre { auto fake = util::pool(this->loc); fake->name = this->indexVar; - fake->type = pts::NamedType::create(this->loc, INT64_TYPE_STRING); + fake->type = pts::NamedType::create(this->loc, INTUNSPEC_TYPE_STRING); ret->indexVar = dcast(sst::VarDefn, fake->typecheck(fs).defn()); iceAssert(ret->indexVar); diff --git a/source/typecheck/misc.cpp b/source/typecheck/misc.cpp new file mode 100644 index 00000000..5c6317f0 --- /dev/null +++ b/source/typecheck/misc.cpp @@ -0,0 +1,65 @@ +// misc.cpp +// Copyright (c) 2019, zhiayang +// Licensed under the Apache License Version 2.0. + +#include "pts.h" +#include "ast.h" +#include "errors.h" +#include "typecheck.h" +#include "ir/type.h" + +#include "mpool.h" + + +TCResult ast::PlatformDefn::typecheck(sst::TypecheckState* fs, fir::Type* infer) +{ + fs->pushLoc(this); + defer(fs->popLoc()); + + if(this->defnType == Type::Intrinsic) + { + this->intrinsicDefn->isIntrinsic = true; + return this->intrinsicDefn->typecheck(fs, infer); + } + else if(this->defnType == Type::IntegerType) + { + auto defn = util::pool(this->loc); + // auto opty = fir::OpaqueType::get(this->typeName, this->typeSizeInBits); + auto ty = fir::PrimitiveType::getUintN(this->typeSizeInBits); + + defn->type = ty; + defn->id = Identifier(this->typeName, IdKind::Type); + defn->id.scope = fs->getCurrentScope(); + + fs->checkForShadowingOrConflictingDefinition(defn, [](sst::TypecheckState* fs, sst::Defn* other) -> bool { return true; }); + + // add it first so we can use it in the method bodies, + // and make pointers to it + { + fs->getTreeOfScope(defn->id.scope)->addDefinition(this->typeName, defn, { }); + fs->typeDefnMap[ty] = defn; + } + + return TCResult(defn); + } + else + { + return TCResult(SimpleError::make(this->loc, "nani?")); + } +} + + + + + + + + + + + + + + + + diff --git a/source/typecheck/operators.cpp b/source/typecheck/operators.cpp index 0456033e..c5ac3277 100644 --- a/source/typecheck/operators.cpp +++ b/source/typecheck/operators.cpp @@ -1,5 +1,5 @@ // operators.cpp -// Copyright (c) 2017, zhiayang@gmail.com +// Copyright (c) 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "ast.h" diff --git a/source/typecheck/polymorph/driver.cpp b/source/typecheck/polymorph/driver.cpp index 7adeaa53..d481b23a 100644 --- a/source/typecheck/polymorph/driver.cpp +++ b/source/typecheck/polymorph/driver.cpp @@ -1,5 +1,5 @@ // driver.cpp -// Copyright (c) 2017, zhiayang@gmail.com +// Copyright (c) 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "ast.h" @@ -119,7 +119,7 @@ namespace poly } } - std::set fieldset; + std::vector fields; util::hash_map fieldNames; { size_t i = 0; @@ -127,13 +127,13 @@ namespace poly { auto nm = std::get<0>(f); - fieldset.insert(nm); + fields.push_back(nm); fieldNames[nm] = i++; } } - auto [ seen, err ] = resolver::verifyStructConstructorArguments(fs->loc(), str->name, fieldset, input); + auto [ seen, err ] = resolver::verifyStructConstructorArguments(fs->loc(), str->name, fields, input); if(err) return { soln, err }; diff --git a/source/typecheck/polymorph/instantiator.cpp b/source/typecheck/polymorph/instantiator.cpp index 9becb2df..afd83e07 100644 --- a/source/typecheck/polymorph/instantiator.cpp +++ b/source/typecheck/polymorph/instantiator.cpp @@ -1,5 +1,5 @@ // instantiator.cpp -// Copyright (c) 2017, zhiayang@gmail.com +// Copyright (c) 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "ast.h" diff --git a/source/typecheck/polymorph/misc.cpp b/source/typecheck/polymorph/misc.cpp index 2c672664..2687025c 100644 --- a/source/typecheck/polymorph/misc.cpp +++ b/source/typecheck/polymorph/misc.cpp @@ -1,5 +1,5 @@ // misc.cpp -// Copyright (c) 2017, zhiayang@gmail.com +// Copyright (c) 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "pts.h" @@ -225,6 +225,36 @@ std::pair ast::Parameterisable::checkForExistingDeclaration(ss } +std::string PolyArgMapping_t::print() const +{ + std::string ret; + for(const auto& m : this->maps) + { + ret += ", "; + if(!m.name.empty()) + ret += strprintf("%s: ", m.name); + + ret += m.type->str(); + } + + if(!ret.empty()) + ret = ret.erase(0, 2); + + return ret; +} + + + + + + + + + + + + + diff --git a/source/typecheck/polymorph/solver.cpp b/source/typecheck/polymorph/solver.cpp index e7004494..65288459 100644 --- a/source/typecheck/polymorph/solver.cpp +++ b/source/typecheck/polymorph/solver.cpp @@ -1,5 +1,5 @@ // solver.cpp -// Copyright (c) 2017, zhiayang@gmail.com +// Copyright (c) 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "pts.h" diff --git a/source/typecheck/polymorph/transforms.cpp b/source/typecheck/polymorph/transforms.cpp index d1dab057..d5ef1e4b 100644 --- a/source/typecheck/polymorph/transforms.cpp +++ b/source/typecheck/polymorph/transforms.cpp @@ -1,5 +1,5 @@ // transforms.cpp -// Copyright (c) 2017, zhiayang@gmail.com +// Copyright (c) 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "pts.h" @@ -36,7 +36,7 @@ namespace poly fir::LocatedType Solution_t::getSolution(const std::string& n) const { if(auto it = this->solutions.find(n); it != this->solutions.end()) - return it->second; + return fir::LocatedType(it->second); else return fir::LocatedType(0); diff --git a/source/typecheck/ranges.cpp b/source/typecheck/ranges.cpp index add1de9b..f32ae6af 100644 --- a/source/typecheck/ranges.cpp +++ b/source/typecheck/ranges.cpp @@ -1,5 +1,5 @@ // ranges.cpp -// Copyright (c) 2017, zhiayang@gmail.com +// Copyright (c) 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "ast.h" @@ -18,20 +18,20 @@ TCResult ast::RangeExpr::typecheck(sst::TypecheckState* fs, fir::Type* infer) auto ret = util::pool(this->loc, fir::RangeType::get()); ret->halfOpen = this->halfOpen; - ret->start = this->start->typecheck(fs, fir::Type::getInt64()).expr(); + ret->start = this->start->typecheck(fs, fir::Type::getNativeWord()).expr(); if(!ret->start->type->isIntegerType()) error(ret->start, "expected integer type in range expression (start), found '%s' instead", ret->start->type); - ret->end = this->end->typecheck(fs, fir::Type::getInt64()).expr(); + ret->end = this->end->typecheck(fs, fir::Type::getNativeWord()).expr(); if(!ret->end->type->isIntegerType()) error(ret->end, "expected integer type in range expression (end), found '%s' instead", ret->end->type); if(this->step) { - ret->step = this->step->typecheck(fs, fir::Type::getInt64()).expr(); + ret->step = this->step->typecheck(fs, fir::Type::getNativeWord()).expr(); if(!ret->step->type->isIntegerType()) error(ret->step, "expected integer type in range expression (step), found '%s' instead", ret->step->type); } return TCResult(ret); -} \ No newline at end of file +} diff --git a/source/typecheck/resolver/driver.cpp b/source/typecheck/resolver/driver.cpp index 83a216ff..f12da30e 100644 --- a/source/typecheck/resolver/driver.cpp +++ b/source/typecheck/resolver/driver.cpp @@ -1,5 +1,5 @@ // call.cpp -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "pts.h" @@ -56,7 +56,7 @@ namespace resolver { // unify the handling of generic and non-generic stuff. // if we provided mappings, don't bother searching normal functions. - if(gmaps.empty()) + // if(gmaps.empty()) { auto defs = tree->getDefinitionsWithName(name); for(auto d : defs) @@ -116,7 +116,7 @@ namespace resolver { if(!didGeneric) { - errs = SimpleError::make(fs->loc(), "no such function named '%s'", name); + errs = SimpleError::make(fs->loc(), "no function named '%s' in the current scope", name); } return TCResult(errs); @@ -141,7 +141,7 @@ namespace resolver { return TCResult( SimpleError::make(fs->loc(), "'%s' cannot be called as a function; it was defined with type '%s' in the current scope", - name, def->type)->append(SimpleError::make(def->loc, "Previously defined here:")) + name, def->type)->append(SimpleError::make(def->loc, "previously defined here:")) ); } } @@ -205,17 +205,38 @@ namespace resolver } else if(auto str = dcast(StructDefn, typedf)) { - std::set fieldNames; - for(auto f : str->fields) - fieldNames.insert(f->id.name); + std::vector fieldNames; - auto err = resolver::verifyStructConstructorArguments(fs->loc(), str->id.name, fieldNames, arguments); + for(auto f : str->fields) + fieldNames.push_back(f->id.name); - if(err.second != nullptr) - return TCResult(err.second); + auto [ seen, err ] = resolver::verifyStructConstructorArguments(fs->loc(), str->id.name, fieldNames, arguments); - // in actual fact we just return the thing here. sigh. - return TCResult(str); + if(err != nullptr) + { + return TCResult(err); + } + else + { + auto seencopy = seen; + std::vector target = util::filterMap(str->fields, [&seencopy](sst::StructFieldDefn* f) -> bool { + return seencopy.find(f->id.name) != seencopy.end(); + }, [](sst::StructFieldDefn* f) -> FnParam { + return FnParam(f->loc, f->id.name, f->type); + }); + + ErrorMsg* _err = 0; + auto args = resolver::misc::canonicaliseCallArguments(fs->loc(), target, arguments, &_err); + if(_err != 0) return TCResult(_err); + + auto [ soln, err ] = poly::solveTypeList(util::map(target, [](const FnParam& f) -> fir::LocatedType { + return fir::LocatedType(f.type, f.loc); + }), args, poly::Solution_t(), /* isFnCall: */ true); + + // in actual fact we just return the thing here. sigh. + if(err != nullptr) return TCResult(err); + else return TCResult(str); + } } else if(auto uvd = dcast(sst::UnionVariantDefn, typedf)) { @@ -232,11 +253,17 @@ namespace resolver return ret; } + else if(auto rud = dcast(sst::RawUnionDefn, typedf)) + { + return TCResult(SimpleError::make(fs->loc(), "constructors are not defined for raw unions") + ->append(SimpleError::make(MsgType::Note, rud->loc, "type was defined here:")) + ); + } else { return TCResult( SimpleError::make(fs->loc(), "unsupported constructor call on type '%s'", typedf->id.name) - ->append(SimpleError::make(typedf->loc, "type was defined here:")) + ->append(SimpleError::make(MsgType::Note, typedf->loc, "type was defined here:")) ); } } @@ -292,10 +319,10 @@ namespace resolver error(arguments[0].loc, "first argument to two-arg string initialiser (data pointer) must be '%s' or '%s', found '%s' instead", fir::Type::getInt8Ptr(), fir::Type::getMutInt8Ptr(), t1); } - else if(auto t2 = arguments[1].value->type; fir::getCastDistance(t2, fir::Type::getInt64()) < 0) + else if(auto t2 = arguments[1].value->type; fir::getCastDistance(t2, fir::Type::getNativeWord()) < 0) { error(arguments[0].loc, "second argument to two-arg string initialiser (length) must be '%s', found '%s' instead", - (fir::Type*) fir::Type::getInt64(), t2); + (fir::Type*) fir::Type::getNativeWord(), t2); } else { diff --git a/source/typecheck/resolver/misc.cpp b/source/typecheck/resolver/misc.cpp index be9c3b87..9e0dd8c6 100644 --- a/source/typecheck/resolver/misc.cpp +++ b/source/typecheck/resolver/misc.cpp @@ -1,5 +1,5 @@ // misc.cpp -// Copyright (c) 2017, zhiayang@gmail.com +// Copyright (c) 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "sst.h" @@ -206,11 +206,8 @@ namespace sst std::pair, ErrorMsg*> verifyStructConstructorArguments(const Location& callLoc, - const std::string& name, const std::set& fieldNames, const std::vector& arguments) + const std::string& name, const std::vector& fieldNames, const std::vector& arguments) { - // ok, structs get named arguments, and no un-named arguments. - // we just loop through each argument, ensure that (1) every arg has a name; (2) every name exists in the struct - //* note that structs don't have inline member initialisers, so there's no trouble with this approach (in the codegeneration) //* of inserting missing arguments as just '0' or whatever their default value is @@ -224,42 +221,47 @@ namespace sst util::hash_map seenNames; for(auto arg : arguments) { - if(arg.name.empty() && useNames) + if((arg.name.empty() && useNames) || (!firstName && !useNames && !arg.name.empty())) { return { { }, SimpleError::make(arg.loc, "named arguments cannot be mixed with positional arguments in a struct constructor") }; } else if(firstName && !arg.name.empty()) { useNames = true; - firstName = false; } - else if(!arg.name.empty() && !useNames && !firstName) - { - return { { }, SimpleError::make(arg.loc, "named arguments cannot be mixed with positional arguments in a struct constructor") }; - } - else if(useNames && fieldNames.find(arg.name) == fieldNames.end()) + + + if(!arg.name.empty() && std::find(fieldNames.begin(), fieldNames.end(), arg.name) == fieldNames.end()) { return { { }, SimpleError::make(arg.loc, "field '%s' does not exist in struct '%s'", arg.name, name) }; } - else if(useNames && seenNames.find(arg.name) != seenNames.end()) + else if(!arg.name.empty() && seenNames.find(arg.name) != seenNames.end()) { return { { }, SimpleError::make(arg.loc, "duplicate argument for field '%s' in constructor call to struct '%s'", arg.name, name) }; } + firstName = false; seenNames[arg.name] = ctr; ctr += 1; } //* note: if we're doing positional args, allow only all or none. - if(!useNames && arguments.size() != fieldNames.size() && arguments.size() > 0) + if(!useNames) { - return { { }, SimpleError::make(callLoc, - "mismatched number of arguments in constructor call to type '%s'; expected %d arguments, found %d arguments instead", - name, fieldNames.size(), arguments.size())->append( - BareError::make(MsgType::Note, "all arguments are mandatory when using positional arguments") - ) - }; + if(arguments.size() != fieldNames.size() && arguments.size() > 0) + { + return { { }, SimpleError::make(callLoc, + "mismatched number of arguments in constructor call to type '%s'; expected %d, found %d instead", + name, fieldNames.size(), arguments.size())->append( + BareError::make(MsgType::Note, "all arguments are mandatory when using positional arguments") + ) + }; + } + + // ok; populate 'seenNames' with all the fields, because we 'saw' them, I guess. + for(size_t i = 0; i < fieldNames.size(); i++) + seenNames[fieldNames[i]] = i; } return { seenNames, nullptr }; diff --git a/source/typecheck/resolver/resolver.cpp b/source/typecheck/resolver/resolver.cpp index 426f2dc1..3b5e3dee 100644 --- a/source/typecheck/resolver/resolver.cpp +++ b/source/typecheck/resolver/resolver.cpp @@ -1,5 +1,5 @@ // resolver.cpp -// Copyright (c) 2017, zhiayang@gmail.com +// Copyright (c) 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "sst.h" @@ -66,6 +66,17 @@ namespace resolver auto cands = _cands; + + auto complainAboutExtraneousPAMs = [&fs](const std::string& kind, Defn* def, const std::string& action, bool printdef) -> ErrorMsg* { + auto ret = SimpleError::make(fs->loc(), "%s '%s' cannot be %s with type arguments", + kind, def->id.name, action); + + if(printdef) + ret->append(SimpleError::make(MsgType::Note, def->loc, "function was defined here:")); + + return ret; + }; + for(const auto& [ _cand, _args ] : cands) { int dist = -1; @@ -131,7 +142,17 @@ namespace resolver } else { - std::tie(dist, fails[fn]) = computeNamedOverloadDistance(fn->loc, fn->params, replacementArgs, fn->isVarArg); + // if it's not generic but you gave type args, you don't deserve to call it. + //? we might change this + + if(!pams.empty()) + { + fails[fn] = complainAboutExtraneousPAMs("non-polymorphic function", fn, "called", /* printdef: */ true); + } + else + { + std::tie(dist, fails[fn]) = computeNamedOverloadDistance(fn->loc, fn->params, replacementArgs, fn->isVarArg); + } } //! SELF HANDLING (REMOVAL) (METHOD CALL) @@ -143,6 +164,12 @@ namespace resolver iceAssert(vr->type->isFunctionType()); auto ft = vr->type->toFunctionType(); + if(!pams.empty()) + { + fails[vr] = complainAboutExtraneousPAMs("variables", vr, "used", /* printdef: */ false); + continue; + } + // check if have any names for(auto p : replacementArgs) { @@ -163,18 +190,40 @@ namespace resolver } else if(auto td = dcast(TypeDefn, curcandidate)) { + if(!pams.empty()) + { + if(!td->type->containsPlaceholders()) + { + fails[td] = complainAboutExtraneousPAMs("non-polymorphic type", td, "constructed", /* printdef: */ true); + continue; + } + else if(auto uvd = dcast(UnionVariantDefn, curcandidate)) + { + // fails[td] = complainAboutExtraneousPAMs("non-polymorphic type", td, "constructed", /* printdef: */ true); + fails[td] = SimpleError::make(fs->loc(), "type arguments should be specified on the union instead of the variant") + ->append(ExampleMsg::make(strprintf("%s!<%s>::%s(...)", uvd->parentUnion->bareName, pams.print(), + uvd->variantName)) + ); + continue; + } + } + auto res = resolveConstructorCall(fs, td, replacementArgs, pams); if(!res.isDefn()) { - fails[fn] = res.error(); + fails[td] = res.error(); dist = -1; } else { curcandidate = res.defn(); - std::tie(dist, fails[fn]) = std::make_tuple(0, nullptr); + std::tie(dist, fails[td]) = std::make_tuple(0, nullptr); } } + else + { + fails[curcandidate] = SimpleError::make(fs->loc(), "unsupported entity '%s'", curcandidate->getKind()); + } if(dist == -1) continue; @@ -190,23 +239,36 @@ namespace resolver if(finals.empty()) { - iceAssert(cands.size() == fails.size()); - std::vector tmp = util::map(cands[0].second, [](const FnCallArgument& p) -> auto { return p.value->type; }); + // if we only had one candidate, there are no 'overloads' -- don't be a c++ and say stupid things. + // we just directly post the error message instead. - auto errs = OverloadError::make(SimpleError::make(callLoc, "no overload in call to '%s(%s)' amongst %zu %s", - cands[0].first->id.name, fir::Type::typeListToString(tmp), fails.size(), util::plural("candidate", fails.size()))); - - for(auto f : fails) + if(fails.size() == 1) { - // TODO: HACK -- pass the location around more then!! - // patch in the location if it's not present! - if(auto se = dcast(SimpleError, f.second); se && se->loc == Location()) - se->loc = f.first->loc; - - errs->addCand(f.first, f.second); + return { TCResult(fails.begin()->second), { } }; } + else + { + iceAssert(cands.size() == fails.size()); + std::vector tmp = util::map(cands[0].second, [](const FnCallArgument& p) -> auto { return p.value->type; }); - return { TCResult(errs), { } }; + auto errs = OverloadError::make(SimpleError::make(callLoc, "no overload in call to '%s(%s)' amongst %zu %s", + cands[0].first->id.name, fir::Type::typeListToString(tmp), fails.size(), util::plural("candidate", fails.size()))); + + for(auto f : fails) + { + // TODO: HACK -- pass the location around more then!! + // patch in the location if it's not present! + if(auto se = dcast(SimpleError, f.second); se) + { + se->loc = f.first->loc; + se->msg = "candidate unsuitable: " + se->msg; + } + + errs->addCand(f.first, f.second); + } + + return { TCResult(errs), { } }; + } } else if(finals.size() > 1) { diff --git a/source/typecheck/sizeof.cpp b/source/typecheck/sizeof.cpp index 57884dfd..d2ac8be0 100644 --- a/source/typecheck/sizeof.cpp +++ b/source/typecheck/sizeof.cpp @@ -1,5 +1,5 @@ // sizeof.cpp -// Copyright (c) 2017, zhiayang@gmail.com +// Copyright (c) 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "ast.h" @@ -15,7 +15,7 @@ TCResult ast::SizeofOp::typecheck(sst::TypecheckState* fs, fir::Type* infer) fs->pushLoc(this); defer(fs->popLoc()); - auto ret = util::pool(this->loc, fir::Type::getInt64()); + auto ret = util::pool(this->loc, fir::Type::getNativeWord()); if(dcast(ast::LitNumber, this->expr)) { @@ -37,7 +37,7 @@ TCResult ast::TypeidOp::typecheck(sst::TypecheckState* fs, fir::Type* inferred) fs->pushLoc(this); defer(fs->popLoc()); - auto ret = util::pool(this->loc, fir::Type::getUint64()); + auto ret = util::pool(this->loc, fir::Type::getNativeUWord()); if(dcast(ast::LitNumber, this->expr)) { diff --git a/source/typecheck/slice.cpp b/source/typecheck/slice.cpp index a6a54359..a48d04c9 100644 --- a/source/typecheck/slice.cpp +++ b/source/typecheck/slice.cpp @@ -1,5 +1,5 @@ // slice.cpp -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "ast.h" @@ -55,8 +55,8 @@ TCResult ast::SliceOp::typecheck(sst::TypecheckState* fs, fir::Type* inferred) else error(array, "invalid type '%s' for slice operation", ty); - auto begin = this->start ? this->start->typecheck(fs, fir::Type::getInt64()).expr() : 0; - auto end = this->end ? this->end->typecheck(fs, fir::Type::getInt64()).expr() : 0; + auto begin = this->start ? this->start->typecheck(fs, fir::Type::getNativeWord()).expr() : 0; + auto end = this->end ? this->end->typecheck(fs, fir::Type::getNativeWord()).expr() : 0; if(begin && !begin->type->isIntegerType()) error(begin, "expected integer type for start index of slice; found '%s'", begin->type); diff --git a/source/typecheck/special.cpp b/source/typecheck/special.cpp index f3813fe0..27929778 100644 --- a/source/typecheck/special.cpp +++ b/source/typecheck/special.cpp @@ -1,5 +1,5 @@ // misc.cpp -// Copyright (c) 2017, zhiayang@gmail.com +// Copyright (c) 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "ast.h" diff --git a/source/typecheck/structs.cpp b/source/typecheck/structs.cpp index df462b8f..e2b7edcf 100644 --- a/source/typecheck/structs.cpp +++ b/source/typecheck/structs.cpp @@ -1,5 +1,5 @@ // structs.cpp -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "ast.h" @@ -18,13 +18,13 @@ static void _checkFieldRecursion(sst::TypecheckState* fs, fir::Type* strty, fir: if(field == strty) { SimpleError::make(floc, "composite type '%s' cannot contain a field of its own type; use a pointer.", strty) - ->append(SimpleError::make(MsgType::Note, fs->typeDefnMap[strty]->loc, "Type '%s' was defined here:", strty)) + ->append(SimpleError::make(MsgType::Note, fs->typeDefnMap[strty]->loc, "type '%s' was defined here:", strty)) ->postAndQuit(); } else if(seeing.find(field) != seeing.end()) { SimpleError::make(floc, "recursive definition of field with a non-pointer type; mutual recursion between types '%s' and '%s'", field, strty) - ->append(SimpleError::make(MsgType::Note, fs->typeDefnMap[strty]->loc, "Type '%s' was defined here:", strty)) + ->append(SimpleError::make(MsgType::Note, fs->typeDefnMap[strty]->loc, "type '%s' was defined here:", strty)) ->postAndQuit(); } else if(field->isClassType()) @@ -133,6 +133,8 @@ TCResult ast::StructDefn::generateDeclaration(sst::TypecheckState* fs, fir::Type auto defnname = util::typeParamMapToString(this->name, gmaps); auto defn = util::pool(this->loc); + defn->bareName = this->name; + defn->id = Identifier(defnname, IdKind::Type); defn->id.scope = this->realScope; defn->visibility = this->visibility; diff --git a/source/typecheck/subscript.cpp b/source/typecheck/subscript.cpp index a9ca9c6a..87aa3c20 100644 --- a/source/typecheck/subscript.cpp +++ b/source/typecheck/subscript.cpp @@ -1,5 +1,5 @@ // subscript.cpp -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "ast.h" @@ -67,7 +67,7 @@ TCResult ast::SubscriptDollarOp::typecheck(sst::TypecheckState* fs, fir::Type* i ->add(util::ESpan(arr->loc, "here"))->postAndQuit(); } - return TCResult(util::pool(this->loc, fir::Type::getInt64())); + return TCResult(util::pool(this->loc, fir::Type::getNativeWord())); } diff --git a/source/typecheck/toplevel.cpp b/source/typecheck/toplevel.cpp index 24fce88b..4927398c 100644 --- a/source/typecheck/toplevel.cpp +++ b/source/typecheck/toplevel.cpp @@ -1,9 +1,10 @@ // toplevel.cpp -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "sst.h" #include "ast.h" +#include "pts.h" #include "errors.h" #include "parser.h" #include "frontend.h" @@ -18,7 +19,10 @@ namespace sst { static StateTree* cloneTree(StateTree* clonee, StateTree* surrogateParent, const std::string& filename) { - auto clone = new StateTree(clonee->name, filename, surrogateParent); + auto clone = util::pool(clonee->name, filename, surrogateParent); + clone->treeDefn = util::pool(Location()); + clone->treeDefn->tree = clone; + for(auto sub : clonee->subtrees) clone->subtrees[sub.first] = cloneTree(sub.second, clone, filename); @@ -183,10 +187,119 @@ namespace sst + struct OsStrings + { + std::string name; + std::string vendor; + }; + + static OsStrings getOsStrings() + { + // TODO: handle cygwin/msys/mingw??? + // like how do we want to expose these? at the end of the day the os is still windows... + + OsStrings ret; + + #if defined(_WIN32) + ret.name = "windows"; + ret.vendor = "microsoft"; + #elif __MINGW__ + ret.name = "mingw"; + #elif __CYGWIN__ + ret.name = "cygwin"; + #elif __APPLE__ + ret.vendor = "apple"; + #include "TargetConditionals.h" + #if TARGET_IPHONE_SIMULATOR + ret.name = "iossimulator"; + #elif TARGET_OS_IOS + ret.name = "ios"; + #elif TARGET_OS_WATCH + ret.name = "watchos"; + #elif TARGET_OS_TV + ret.name = "tvos"; + #elif TARGET_OS_OSX + ret.name = "macos"; + #else + #error "unknown apple operating system" + #endif + #elif __ANDROID__ + ret.vendor = "google"; + ret.name = "android"; + #elif __linux__ || __linux || linux + ret.name = "linux"; + #elif __FreeBSD__ + ret.name = "freebsd"; + #elif __OpenBSD__ + ret.name = "openbsd"; + #elif __NetBSD__ + ret.name = "netbsd"; + #elif __DragonFly__ + ret.name = "dragonflybsd"; + #elif __unix__ + ret.name = "unix"; + #elif defined(_POSIX_VERSION) + ret.name = "posix"; + #endif + + return ret; + } + + static void generatePreludeDefinitions(TypecheckState* fs) + { + auto loc = Location(); + auto strings = getOsStrings(); + + fs->pushTree("os"); + defer(fs->popTree()); + + // manually add the definition, because we didn't typecheck a namespace or anything. + fs->stree->parent->addDefinition(fs->stree->name, fs->stree->treeDefn); + + auto strty = fir::Type::getCharSlice(false); + + { + // add the name + auto name_def = util::pool(loc); + name_def->id = Identifier("name", IdKind::Name); + name_def->type = strty; + name_def->global = true; + name_def->immutable = true; + + auto s = util::pool(loc, strty); + s->str = strings.name; + + name_def->init = s; + fs->stree->addDefinition("name", name_def); + } + { + // add the name + auto vendor_def = util::pool(loc); + vendor_def->id = Identifier("vendor", IdKind::Name); + vendor_def->type = strty; + vendor_def->global = true; + vendor_def->immutable = true; + + auto s = util::pool(loc, strty); + s->str = strings.vendor; + + vendor_def->init = s; + fs->stree->addDefinition("vendor", vendor_def); + } + } + + + + + + + + using frontend::CollectorState; - DefinitionTree* typecheck(CollectorState* cs, const parser::ParsedFile& file, const std::vector>& imports) + DefinitionTree* typecheck(CollectorState* cs, const parser::ParsedFile& file, const std::vector>& imports, + bool addPreludeDefinitions) { - StateTree* tree = new sst::StateTree(file.moduleName, file.name, 0); + StateTree* tree = new StateTree(file.moduleName, file.name, 0); tree->treeDefn = util::pool(Location()); tree->treeDefn->tree = tree; @@ -207,6 +320,11 @@ namespace sst else { StateTree* curinspt = insertPoint; + + // iterate through the import-as list, which is a list of nested scopes to import into + // eg we can `import foo as some::nested::namespace`, which means we need to create + // the intermediate trees. + for(const auto& impas : ias) { if(impas == curinspt->name) @@ -219,7 +337,7 @@ namespace sst } else { - auto newinspt = new sst::StateTree(impas, file.name, curinspt); + auto newinspt = util::pool(impas, file.name, curinspt); curinspt->subtrees[impas] = newinspt; auto treedef = util::pool(cs->dtrees[ithing.name]->topLevel->loc); @@ -245,6 +363,9 @@ namespace sst fs->dtree->thingsImported.insert(ithing.name); } + if(addPreludeDefinitions) + generatePreludeDefinitions(fs); + auto tns = dcast(NamespaceDefn, file.root->typecheck(fs).stmt()); iceAssert(tns); diff --git a/source/typecheck/type.cpp b/source/typecheck/type.cpp index fd6ce901..f31cdd21 100644 --- a/source/typecheck/type.cpp +++ b/source/typecheck/type.cpp @@ -1,5 +1,5 @@ // type.cpp -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "ast.h" @@ -30,14 +30,11 @@ namespace sst } else { - if(ty->getMinBits() <= fir::Type::getInt64()->getBitWidth() - 1) - return fir::Type::getInt64(); + if(ty->getMinBits() <= fir::Type::getNativeWord()->getBitWidth() - 1) + return fir::Type::getNativeWord(); - else if(ty->getMinBits() <= fir::Type::getInt128()->getBitWidth() - 1) - return fir::Type::getInt128(); - - else if(ty->isSigned() && ty->getMinBits() <= fir::Type::getUint128()->getBitWidth()) - return fir::Type::getUint128(); + else if(!ty->isSigned() && ty->getMinBits() <= fir::Type::getNativeUWord()->getBitWidth()) + return fir::Type::getNativeUWord(); else error("int overflow"); @@ -310,7 +307,7 @@ namespace sst if(!begin) { if(allowFail) return 0; - else error(this->loc(), "no such scope '%s'", scopes.front()); + else error(this->loc(), "nonexistent scope '%s'", scopes.front()); } @@ -337,7 +334,7 @@ namespace sst if(it == begin->subtrees.end()) { if(allowFail) return 0; - else error(this->loc(), "no such entity '%s' in scope '%s'", scopes.front(), prev); + else error(this->loc(), "no entity '%s' in scope '%s'", scopes.front(), prev); } begin = it->second; diff --git a/source/typecheck/typecheckstate.cpp b/source/typecheck/typecheckstate.cpp index 889e6b09..c5a20ad3 100644 --- a/source/typecheck/typecheckstate.cpp +++ b/source/typecheck/typecheckstate.cpp @@ -1,5 +1,5 @@ // typecheckstate.cpp -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "ast.h" @@ -138,6 +138,7 @@ namespace sst + void TypecheckState::pushTree(const std::string& name, bool createAnonymously) { iceAssert(this->stree); @@ -148,17 +149,15 @@ namespace sst } else { - auto newtree = new StateTree(name, this->stree->topLevelFilename, this->stree, createAnonymously); + auto newtree = util::pool(name, this->stree->topLevelFilename, this->stree, createAnonymously); this->stree->subtrees[name] = newtree; - this->stree = newtree; // make a treedef. newtree->treeDefn = util::pool(Location()); newtree->treeDefn->tree = newtree; - } - // if(!this->locationStack.empty()) - // info(this->loc(), "enter namespace %s in %s", name, this->stree->parent->name); + this->stree = newtree; + } } StateTree* TypecheckState::popTree() @@ -285,7 +284,7 @@ namespace sst if(auto it = tree->subtrees.find(s); it == tree->subtrees.end()) { - error(this->loc(), "no such tree '%s' in scope '%s' (in teleportation to '%s')", s, tree->name, util::serialiseScope(scope)); + error(this->loc(), "nonexistent tree '%s' in scope '%s' (in teleportation to '%s')", s, tree->name, util::serialiseScope(scope)); } else { @@ -370,7 +369,6 @@ namespace sst tree = tree->parent; } - // warn("No such tree '%s' in scope", name); return 0; } diff --git a/source/typecheck/unions.cpp b/source/typecheck/unions.cpp index f0485f5d..21b968d2 100644 --- a/source/typecheck/unions.cpp +++ b/source/typecheck/unions.cpp @@ -1,5 +1,5 @@ // unions.cpp -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "ast.h" @@ -31,6 +31,8 @@ TCResult ast::UnionDefn::generateDeclaration(sst::TypecheckState* fs, fir::Type* if(this->israw) defn = util::pool(this->loc); else defn = util::pool(this->loc); + defn->bareName = this->name; + defn->id = Identifier(defnname, IdKind::Type); defn->id.scope = this->realScope; defn->visibility = this->visibility; @@ -124,7 +126,7 @@ TCResult ast::UnionDefn::typecheck(sst::TypecheckState* fs, fir::Type* infer, co size_t tfn = 0; for(auto [ loc, pty ] : this->transparentFields) { - auto sfd = make_field(strprintf("__transparent_field_%zu", tfn++), loc, pty); + auto sfd = make_field(util::obfuscateName("transparent_field", tfn++), loc, pty); iceAssert(sfd); sfd->isTransparentField = true; diff --git a/source/typecheck/using.cpp b/source/typecheck/using.cpp index 99537b06..e82859c6 100644 --- a/source/typecheck/using.cpp +++ b/source/typecheck/using.cpp @@ -1,5 +1,5 @@ // using.cpp -// Copyright (c) 2017, zhiayang@gmail.com +// Copyright (c) 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "ast.h" diff --git a/source/typecheck/variable.cpp b/source/typecheck/variable.cpp index ab0b1a24..983f0913 100644 --- a/source/typecheck/variable.cpp +++ b/source/typecheck/variable.cpp @@ -1,5 +1,5 @@ // variable.cpp -// Copyright (c) 2014 - 2017, zhiayang@gmail.com +// Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. #include "pts.h" @@ -41,7 +41,7 @@ static TCResult checkPotentialCandidate(sst::TypecheckState* fs, ast::Ident* ide return TCResult( SimpleError::make(ident->loc, "field '%s' is an instance member of type '%s', and cannot be accessed statically", ident->name, fld->parentType->id.name) - ->append(SimpleError::make(MsgType::Note, def->loc, "Field '%s' was defined here:", def->id.name)) + ->append(SimpleError::make(MsgType::Note, def->loc, "field '%s' was defined here:", def->id.name)) ); } } @@ -315,6 +315,8 @@ TCResult ast::VarDefn::typecheck(sst::TypecheckState* fs, fir::Type* infer) iceAssert(defn); + defn->bareName = this->name; + defn->id = Identifier(this->name, IdKind::Name); defn->id.scope = fs->getCurrentScope();