Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/check.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -761,8 +761,8 @@ void NamedAttr::check(TypeChecker& checker, const ast::Node* node) {
auto fn_type = fn_decl->type->isa<artic::FnType>();
if (!fn_type)
checker.error(fn_decl->loc, "polymorphic functions cannot be exported");
else if (fn_decl->type->order() > 1)
checker.error(fn_decl->loc, "higher-order functions cannot be exported");
//else if (fn_decl->type->order() > 1)
// checker.error(fn_decl->loc, "higher-order functions cannot be exported");
else if (!fn_decl->fn->body)
checker.error(fn_decl->loc, "exported functions must have a body");
else
Expand Down
19 changes: 19 additions & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,25 @@ add_test(NAME simple_compare COMMAND artic --print-ast ${CMAKE_CURRENT_SOURC
add_test(NAME simple_double_ptr COMMAND artic --print-ast ${CMAKE_CURRENT_SOURCE_DIR}/simple/double_ptr.art)
add_test(NAME simple_math COMMAND artic --print-ast ${CMAKE_CURRENT_SOURCE_DIR}/simple/math.art)

add_test(NAME thorin_closure_conv_no_op COMMAND artic --print-ast -g --emit-c ${CMAKE_CURRENT_SOURCE_DIR}/thorin/closure_conv_no_op.art)
add_test(NAME thorin_closure_conv_empty_env COMMAND artic --print-ast -g --emit-c ${CMAKE_CURRENT_SOURCE_DIR}/thorin/closure_conv_empty_env.art)
add_test(NAME thorin_closure_conv_thin_env COMMAND artic --print-ast -g --emit-c ${CMAKE_CURRENT_SOURCE_DIR}/thorin/closure_conv_thin_env.art)
add_test(NAME thorin_closure_conv_allocated_env COMMAND artic --print-ast -g --emit-c ${CMAKE_CURRENT_SOURCE_DIR}/thorin/closure_conv_allocated_env.art)
add_test(NAME thorin_closure_conv_capture1 COMMAND artic --print-ast -g --emit-c ${CMAKE_CURRENT_SOURCE_DIR}/thorin/closure_conv_capture1.art)
add_test(NAME thorin_closure_conv_capture2 COMMAND artic --print-ast -g --emit-c ${CMAKE_CURRENT_SOURCE_DIR}/thorin/closure_conv_capture2.art)
add_test(NAME thorin_closure_conv_capture_bb COMMAND artic --print-ast -g --emit-c ${CMAKE_CURRENT_SOURCE_DIR}/thorin/closure_conv_capture_bb.art)
add_test(NAME thorin_closure_conv_capture_top_level COMMAND artic --print-ast -g --emit-c ${CMAKE_CURRENT_SOURCE_DIR}/thorin/closure_conv_capture_top_level.art)
add_test(NAME thorin_closure_conv_rec COMMAND artic --print-ast -g --emit-c ${CMAKE_CURRENT_SOURCE_DIR}/thorin/closure_conv_rec.art)
add_test(NAME thorin_closure_conv_rec2 COMMAND artic --print-ast -g --emit-c ${CMAKE_CURRENT_SOURCE_DIR}/thorin/closure_conv_rec2.art)
add_test(NAME thorin_range_loop1 COMMAND artic --print-ast -g --emit-c ${CMAKE_CURRENT_SOURCE_DIR}/thorin/range_loop1.art)
add_test(NAME thorin_range_loop2 COMMAND artic --print-ast -g --emit-c ${CMAKE_CURRENT_SOURCE_DIR}/thorin/range_loop2.art)
add_test(NAME thorin_range_loop3 COMMAND artic --print-ast -g --emit-c ${CMAKE_CURRENT_SOURCE_DIR}/thorin/range_loop3.art)
add_test(NAME thorin_parallel_basic COMMAND artic --print-ast -g --emit-llvm ${CMAKE_CURRENT_SOURCE_DIR}/thorin/parallel_basic.art)
add_test(NAME thorin_parallel_functions1 COMMAND artic --print-ast -g --emit-llvm ${CMAKE_CURRENT_SOURCE_DIR}/thorin/parallel_functions1.art)
add_test(NAME thorin_parallel_functions2 COMMAND artic --print-ast -g --emit-llvm ${CMAKE_CURRENT_SOURCE_DIR}/thorin/parallel_functions2.art)
add_test(NAME thorin_parallel_functions3 COMMAND artic --print-ast -g --emit-llvm ${CMAKE_CURRENT_SOURCE_DIR}/thorin/parallel_functions3.art)
add_test(NAME thorin_busted_scheduler COMMAND artic --print-ast -g --emit-llvm ${CMAKE_CURRENT_SOURCE_DIR}/thorin/busted_scheduler.art)

add_failure_test(NAME failure_annot COMMAND artic ${CMAKE_CURRENT_SOURCE_DIR}/failure/annot.art)
add_failure_test(NAME failure_comment COMMAND artic ${CMAKE_CURRENT_SOURCE_DIR}/failure/comment.art)
add_failure_test(NAME failure_utf8 COMMAND artic ${CMAKE_CURRENT_SOURCE_DIR}/failure/utf8.art)
Expand Down
4 changes: 4 additions & 0 deletions test/codegen/helpers.c
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,7 @@ void save_img(int32_t w, int32_t h, const double* img) {
}
free(out_row);
}

void* anydsl_alloc(int32_t dont_care, int64_t size) {
return malloc(size);
}
14 changes: 7 additions & 7 deletions test/thorin/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ macro(assign_me_bool var)
endif()
endmacro()

function(add_thorin_test)
function(add_backend_test)
cmake_parse_arguments(test "NO_C;NO_LLVM;NO_SPIRV" "NAME;SOURCE_FILE" "ARGS" ${ARGN})
assign_me_bool(TEST_USE_C NOT ${test_NO_C})
assign_me_bool(HAS_LLVM ${Thorin_HAS_LLVM_SUPPORT})
assign_me_bool(TEST_USE_LLVM ${HAS_LLVM} AND NOT ${test_NO_LLVM})
assign_me_bool(HAS_SPIRV ${Thorin_HAS_SPIRV_SUPPORT})
assign_me_bool(TEST_USE_SPIRV ${HAS_SPIRV} AND NOT ${test_NO_SPIRV})

add_test(NAME thorin_${test_NAME} COMMAND ${CMAKE_COMMAND}
add_test(NAME backend_${test_NAME} COMMAND ${CMAKE_COMMAND}
-DCOMPILER=$<TARGET_FILE:artic>
-DC_COMPILER=${CMAKE_C_COMPILER}
-DT=${test_NAME}
Expand All @@ -30,8 +30,8 @@ function(add_thorin_test)
-P ${PROJECT_SOURCE_DIR}/test/thorin/oracle.cmake)
endfunction()

add_thorin_test(NAME hello_world SOURCE_FILE hello_world)
add_thorin_test(NAME llvm_intrinsic SOURCE_FILE llvm_intrinsic NO_C NO_SPIRV)
add_thorin_test(NAME spirv_builtin SOURCE_FILE spirv_builtin NO_C NO_LLVM)
add_thorin_test(NAME control_flow SOURCE_FILE control_flow)
add_thorin_test(NAME slot SOURCE_FILE slot)
add_backend_test(NAME hello_world SOURCE_FILE hello_world)
add_backend_test(NAME llvm_intrinsic SOURCE_FILE llvm_intrinsic NO_C NO_SPIRV)
add_backend_test(NAME spirv_builtin SOURCE_FILE spirv_builtin NO_C NO_LLVM)
add_backend_test(NAME control_flow SOURCE_FILE control_flow)
add_backend_test(NAME slot SOURCE_FILE slot)
26 changes: 26 additions & 0 deletions test/thorin/busted_scheduler.art
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#[import(cc="C")]
fn bar() -> i32;

#[import(cc="C")]
fn leak(fn(i32) -> i32) -> ();

// this breaks the scheduler on 802e2eafbbe6cd79d4bca47c9553f315bc4568ea
// (with the inlining pass disabled)
// the old early() scheduler was not able to properly cope with recursive aggregates (the closures of f and g)
// and would end up scheduling one of them before `y` is computed.
#[export]
fn foo(z: i32) {
let y = bar();

fn f(x: i32) -> i32 {
g(x + y)
}

fn g(x: i32) -> i32 {
f(x + z)
}

leak(f);
leak(g);
0
}
12 changes: 12 additions & 0 deletions test/thorin/closure_conv_allocated_env.art
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#[import(cc="C")]
fn leak(fn(i32) -> (i32, i32)) -> ();

// this time the environment is too big to fit in a pointer and must be allocated/extracted from
#[export]
fn foo(x: i32, z: i32) -> i32 {
fn f(y: i32) { (x + y, z) }

leak(f);

return (0)
}
9 changes: 9 additions & 0 deletions test/thorin/closure_conv_capture1.art
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#[import(cc="C")]
fn leak2(i32, fn(i32) -> !) -> ();

// we capture the return continuation of foo here
#[export]
fn foo() -> i32 {
leak2(5, return);
0
}
13 changes: 13 additions & 0 deletions test/thorin/closure_conv_capture2.art
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#[import(cc="C")]
fn leak2(i32, fn() -> !) -> ();

// similar to the last one but now we capture a 'break' continuation
#[export]
fn foo() -> i32 {
while true {
leak2(5, break);
}
// this basic block is unreachable from the CFG
// but should still be emitted as part of this foo's scope
0
}
18 changes: 18 additions & 0 deletions test/thorin/closure_conv_capture3.art
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#[import(cc="C")]
fn out(i32) -> ();

fn bar(a: i32, b: fn() -> ()) -> () {
out(a);
b()
}

// we capture the return continuation of foo here
#[export]
fn foo(x: i32, y: &i32) -> i32 {
while true {
//bar(5, continue);
bar(5, break);
bar(5, break);
}
x + *y
}
20 changes: 20 additions & 0 deletions test/thorin/closure_conv_capture_bb.art
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#[import(cc="C")]
fn leak(fn(i32) -> !) -> ();

#[export]
fn foo(x: i32) -> i32 {
let r = return;

// 'f' is a basic block within 'foo'
fn f(y: i32) -> ! {
if (x + y > 0) {
r(0)
} else {
r(y)
}
}

leak(f);

return (0)
}
11 changes: 11 additions & 0 deletions test/thorin/closure_conv_capture_top_level.art
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#[import(cc="C")]
fn leak(fn(i32) -> i32) -> ();

#[export]
fn foo(x: i32) -> i32 {
fn f(y: i32) { 0 }

leak(f);

return (0)
}
11 changes: 11 additions & 0 deletions test/thorin/closure_conv_empty_env.art
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#[import(cc="C")]
fn leak(fn(i32) -> i32) -> ();

#[export]
fn foo(x: i32) -> i32 {
fn f(y: i32) { x }

leak(f);

return (0)
}
5 changes: 5 additions & 0 deletions test/thorin/closure_conv_no_op.art
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// no actual closure conversion takes place, but the type of 'f' should be affected
#[export]
fn foo(f: fn(i32) -> i32) -> i32 {
f(42)
}
21 changes: 21 additions & 0 deletions test/thorin/closure_conv_rec.art
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#[import(cc="C")]
fn leak(fn(i32) -> i32) -> ();

// we have two nested closures that can call each other
// this requires the closure for g to consume f's "self" parameter
// otherwise closure conversion just diverges
#[export]
fn foo(i: i32) -> i32 {
fn f(x: i32) -> i32 {
fn g(y: i32) -> i32 {
f(x + y + i)
}

leak(g);
g(x)
}

leak(f);

0
}
21 changes: 21 additions & 0 deletions test/thorin/closure_conv_rec2.art
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#[import(cc="C")]
fn leak(fn(i32) -> i32) -> ();

// we have two closures who can call each other, neither is nested in the other.
// this results in two mutually recursive closures: their environments include the other.
// this is unequivocally painful to deal with.
#[export]
fn foo(i: i32) -> i32 {
fn f(x: i32) -> i32 {
g(x * i)
}

fn g(y: i32) -> i32 {
f(y + i)
}

leak(f);
leak(g);

0
}
11 changes: 11 additions & 0 deletions test/thorin/closure_conv_thin_env.art
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#[import(cc="C")]
fn leak(fn(i32) -> i32) -> ();

#[export]
fn foo(x: i32) -> i32 {
fn f(y: i32) { x + y }

leak(f);

return (0)
}
10 changes: 10 additions & 0 deletions test/thorin/parallel_basic.art
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#[import(cc = "thorin", name = "parallel")] fn thorin_parallel(_num_threads: i32, _lower: i32, _upper: i32, _body: fn(i32) -> ()) -> ();
fn @parallel(body: fn(i32) -> ()) = @|num_threads: i32, lower: i32, upper: i32| thorin_parallel(num_threads, lower, upper, body);

#[export]
fn parallel_test(n: i32, a: &[i32], b: &[i32], c: &mut[i32]) -> i32 {
for i in parallel(4, 0, n) {
c(i) = a(i) + b(i);
}
0
}
12 changes: 12 additions & 0 deletions test/thorin/parallel_functions1.art
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#[import(cc = "thorin", name = "parallel")] fn thorin_parallel(_num_threads: i32, _lower: i32, _upper: i32, _body: fn(i32) -> ()) -> ();
fn @parallel(body: fn(i32) -> ()) = @|num_threads: i32, lower: i32, upper: i32| thorin_parallel(num_threads, lower, upper, body);

fn mul(x: i32, y: i32) = x * y;

#[export]
fn parallel_test(n: i32, a: &[i32], b: &[i32], c: &mut[i32]) -> i32 {
for i in parallel(4, 0, n) {
c(i) = mul(a(i), a(i)) + mul(b(i), b(i));
}
0
}
17 changes: 17 additions & 0 deletions test/thorin/parallel_functions2.art
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#[import(cc = "thorin", name = "parallel")] fn thorin_parallel(_num_threads: i32, _lower: i32, _upper: i32, _body: fn(i32) -> ()) -> ();
fn @parallel(body: fn(i32) -> ()) = @|num_threads: i32, lower: i32, upper: i32| thorin_parallel(num_threads, lower, upper, body);

#[import(cc="C")]
fn leak(fn(i32) -> i32) -> ();

#[export]
fn parallel_test(n: i32, a: &[i32], b: &[i32], c: &mut[i32]) -> i32 {
fn f(x: i32) = x * n;

for i in parallel(4, 0, n) {
// this requires lifting the local continuation 'f'
leak(f);
c(i) = f(a(i)) + f(b(i));
}
0
}
16 changes: 16 additions & 0 deletions test/thorin/parallel_functions3.art
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#[import(cc = "thorin", name = "parallel")] fn thorin_parallel(_num_threads: i32, _lower: i32, _upper: i32, _body: fn(i32) -> ()) -> ();
fn @parallel(body: fn(i32) -> ()) = @|num_threads: i32, lower: i32, upper: i32| thorin_parallel(num_threads, lower, upper, body);

#[import(cc="C")]
fn magic() -> fn(i32) -> i32;

#[export]
fn parallel_test(n: i32, a: &[i32], b: &[i32], c: &mut[i32]) -> i32 {
// this produces a function pointer, it should be spillable without CC
let f = magic();

for i in parallel(4, 0, n) {
c(i) = f(a(i)) + f(b(i));
}
0
}
21 changes: 21 additions & 0 deletions test/thorin/parallel_test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

void parallel_test(int, int*, int*, int*);

int main(int argc, char** argv) {
int n = 4096 * 1024; // 4M elements
int* a = malloc(sizeof(int) * n);
int* b = malloc(sizeof(int) * n);
int* c = malloc(sizeof(int) * n);
for (int i = 0; i < n; i++) {
a[i] = i % 100;
b[i] = 100 - i % 100;
}
parallel_test(n, a, b, c);
for (int i = 0; i < n; i++) {
assert(c[i] == 100);
}
printf("It werks.\n");
}
20 changes: 20 additions & 0 deletions test/thorin/range_loop1.art
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
fn @range(body: fn (i32) -> ()) {
fn loop(a: i32, b: i32) -> () {
if a < b {
body(a);
loop(a + 1, b)
}
}
loop
}

#[export]
fn foo(count: i32, buf: &mut[i32]) -> i32 {
let mut acc = 0;
fn inner(i: i32) {
acc += buf(i);
}
range(inner)(0, count);
acc
}

21 changes: 21 additions & 0 deletions test/thorin/range_loop2.art
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
fn @range(body: fn (i32) -> ()) {
fn loop(a: i32, b: i32) -> () {
if a < b {
body(a);
loop(a + 1, b)
}
}
loop
}

#[export]
fn foo(count: i32, buf: &mut[i32]) -> i32 {
let mut acc = 0;
fn inner(i: i32) {
acc += buf(i);
}
range(inner)(0, count);
range(inner)(0, count); // we now use 'inner' in two places so it cannot be inlined!
acc
}

Loading
Loading