// #target: macos-x64
module test;
import libc;
import std::io;

macro @timeit(#call)
{
    long t = (long)libc::clock();
    var $Type = $typeof(#call);
    var $is_void = $Type.typeid == void.typeid;
	$if $is_void:
        #call;
	$else
        $Type result = #call;
	$endif
    long diff = (long)libc::clock() - t;
    libc::printf("'%s' took %lld us\n", $stringify(#call), diff);
	$if !$is_void:
        return result;
	$endif
}

fn void test()
{
    for (int i = 0; i < 1000; i++) libc::printf("%d\n", i);
}

fn void main()
{
    @timeit(test());
    int a = 100;
    int x = @timeit(1 + 3 + a);
    libc::printf("Result was %d\n", x);
}

/* #expect: test.ll

@.str = private unnamed_addr constant [4 x i8] c"%d\0A\00", align 1
@.str.1 = private unnamed_addr constant [19 x i8] c"'%s' took %lld us\0A\00", align 1
@.str.2 = private unnamed_addr constant [7 x i8] c"test()\00", align 1
@.str.3 = private unnamed_addr constant [19 x i8] c"'%s' took %lld us\0A\00", align 1
@.str.4 = private unnamed_addr constant [10 x i8] c"1 + 3 + a\00", align 1
@.str.5 = private unnamed_addr constant [15 x i8] c"Result was %d\0A\00", align 1

define void @test.test() #0 {
entry:
  %i = alloca i32, align 4
  store i32 0, ptr %i, align 4
  br label %loop.cond

loop.cond:                                        ; preds = %loop.body, %entry
  %0 = load i32, ptr %i, align 4
  %lt = icmp slt i32 %0, 1000
  br i1 %lt, label %loop.body, label %loop.exit

loop.body:                                        ; preds = %loop.cond
  %1 = load i32, ptr %i, align 4
  %2 = call i32 (ptr, ...) @printf(ptr @.str, i32 %1)
  %3 = load i32, ptr %i, align 4
  %add = add i32 %3, 1
  store i32 %add, ptr %i, align 4
  br label %loop.cond

loop.exit:                                        ; preds = %loop.cond
  ret void
}

define void @test.main() #0 {
entry:
  %t = alloca i64, align 8
  %diff = alloca i64, align 8
  %a = alloca i32, align 4
  %x = alloca i32, align 4
  %t1 = alloca i64, align 8
  %result = alloca i32, align 4
  %diff2 = alloca i64, align 8
  %0 = call i64 @clock()
  store i64 %0, ptr %t, align 8
  call void @test.test()
  %1 = call i64 @clock()
  %2 = load i64, ptr %t, align 8
  %sub = sub i64 %1, %2
  store i64 %sub, ptr %diff, align 8
  %3 = load i64, ptr %diff, align 8
  %4 = call i32 (ptr, ...) @printf(ptr @.str.1, ptr @.str.2, i64 %3)
  store i32 100, ptr %a, align 4
  %5 = call i64 @clock()
  store i64 %5, ptr %t1, align 8
  %6 = load i32, ptr %a, align 4
  %add = add i32 4, %6
  store i32 %add, ptr %result, align 4
  %7 = call i64 @clock()
  %8 = load i64, ptr %t1, align 8
  %sub3 = sub i64 %7, %8
  store i64 %sub3, ptr %diff2, align 8
  %9 = load i64, ptr %diff2, align 8
  %10 = call i32 (ptr, ...) @printf(ptr @.str.3, ptr @.str.4, i64 %9)
  %11 = load i32, ptr %result, align 4
  store i32 %11, ptr %x, align 4
  %12 = load i32, ptr %x, align 4
  %13 = call i32 (ptr, ...) @printf(ptr @.str.5, i32 %12)
  ret void
}

define i32 @main(i32 %0, ptr %1) #0 {
entry:
  call void @test.main()
  ret i32 0
}
