codon/test/transform/folding.codon

695 lines
16 KiB
Python

OP_COUNT = 0
@llvm
def inc(a: int) -> int:
%tmp = add i64 %a, 1
ret i64 %tmp
class I:
@llvm
def __float__(self: int) -> float:
%tmp = sitofp i64 %self to double
ret double %tmp
@llvm
def __bool__(self: int) -> bool:
%0 = icmp ne i64 %self, 0
%1 = zext i1 %0 to i8
ret i8 %1
def __pos__(self: int) -> int:
return self
def __neg__(self: int) -> int:
return I.__sub__(0, self)
@llvm
def __abs__(self: int) -> int:
%0 = icmp sgt i64 %self, 0
%1 = sub i64 0, %self
%2 = select i1 %0, i64 %self, i64 %1
ret i64 %2
@llvm
def __lshift__(self: int, other: int) -> int:
%0 = shl i64 %self, %other
ret i64 %0
@llvm
def __rshift__(self: int, other: int) -> int:
%0 = ashr i64 %self, %other
ret i64 %0
@llvm
def __add__(self: int, other: float) -> float:
%0 = sitofp i64 %self to double
%1 = fadd double %0, %other
ret double %1
@llvm
def __add__(self: int, b: int) -> int:
%tmp = add i64 %self, %b
ret i64 %tmp
@llvm
def __sub__(self: int, other: float) -> float:
%0 = sitofp i64 %self to double
%1 = fsub double %0, %other
ret double %1
@llvm
def __sub__(self: int, b: int) -> int:
%tmp = sub i64 %self, %b
ret i64 %tmp
@llvm
def __mul__(self: int, other: float) -> float:
%0 = sitofp i64 %self to double
%1 = fmul double %0, %other
ret double %1
@llvm
def __mul__(self: int, b: int) -> int:
%tmp = mul i64 %self, %b
ret i64 %tmp
@llvm
def __floordiv__(self: int, other: float) -> float:
declare double @llvm.floor.f64(double)
%0 = sitofp i64 %self to double
%1 = fdiv double %0, %other
%2 = call double @llvm.floor.f64(double %1)
ret double %2
@llvm
def __floordiv__(self: int, b: int) -> int:
%tmp = sdiv i64 %self, %b
ret i64 %tmp
@llvm
def __truediv__(self: int, other: float) -> float:
%0 = sitofp i64 %self to double
%1 = fdiv double %0, %other
ret double %1
@llvm
def __truediv__(self: int, other: int) -> float:
%0 = sitofp i64 %self to double
%1 = sitofp i64 %other to double
%2 = fdiv double %0, %1
ret double %2
@llvm
def __mod__(self: int, other: float) -> float:
%0 = sitofp i64 %self to double
%1 = frem double %0, %other
ret double %1
@llvm
def __mod__(a: int, b: int) -> int:
%tmp = srem i64 %a, %b
ret i64 %tmp
@llvm
def __invert__(a: int) -> int:
%tmp = xor i64 %a, -1
ret i64 %tmp
@llvm
def __and__(a: int, b: int) -> int:
%tmp = and i64 %a, %b
ret i64 %tmp
@llvm
def __or__(a: int, b: int) -> int:
%tmp = or i64 %a, %b
ret i64 %tmp
@llvm
def __xor__(a: int, b: int) -> int:
%tmp = xor i64 %a, %b
ret i64 %tmp
@llvm
def __shr__(a: int, b: int) -> int:
%tmp = ashr i64 %a, %b
ret i64 %tmp
@llvm
def __shl__(a: int, b: int) -> int:
%tmp = shl i64 %a, %b
ret i64 %tmp
@llvm
def __bitreverse__(a: int) -> int:
declare i64 @llvm.bitreverse.i64(i64 %a)
%tmp = call i64 @llvm.bitreverse.i64(i64 %a)
ret i64 %tmp
@llvm
def __bswap__(a: int) -> int:
declare i64 @llvm.bswap.i64(i64 %a)
%tmp = call i64 @llvm.bswap.i64(i64 %a)
ret i64 %tmp
@llvm
def __ctpop__(a: int) -> int:
declare i64 @llvm.ctpop.i64(i64 %a)
%tmp = call i64 @llvm.ctpop.i64(i64 %a)
ret i64 %tmp
@llvm
def __ctlz__(a: int) -> int:
declare i64 @llvm.ctlz.i64(i64 %a, i1 %is_zero_undef)
%tmp = call i64 @llvm.ctlz.i64(i64 %a, i1 false)
ret i64 %tmp
@llvm
def __cttz__(a: int) -> int:
declare i64 @llvm.cttz.i64(i64 %a, i1 %is_zero_undef)
%tmp = call i64 @llvm.cttz.i64(i64 %a, i1 false)
ret i64 %tmp
@llvm
def __eq__(self: int, b: float) -> bool:
%0 = sitofp i64 %self to double
%1 = fcmp oeq double %0, %b
%2 = zext i1 %1 to i8
ret i8 %2
@llvm
def __eq__(a: int, b: int) -> bool:
%tmp = icmp eq i64 %a, %b
%res = zext i1 %tmp to i8
ret i8 %res
@llvm
def __ne__(self: int, b: float) -> bool:
%0 = sitofp i64 %self to double
%1 = fcmp one double %0, %b
%2 = zext i1 %1 to i8
ret i8 %2
@llvm
def __ne__(a: int, b: int) -> bool:
%tmp = icmp ne i64 %a, %b
%res = zext i1 %tmp to i8
ret i8 %res
@llvm
def __lt__(self: int, b: float) -> bool:
%0 = sitofp i64 %self to double
%1 = fcmp olt double %0, %b
%2 = zext i1 %1 to i8
ret i8 %2
@llvm
def __lt__(a: int, b: int) -> bool:
%tmp = icmp slt i64 %a, %b
%res = zext i1 %tmp to i8
ret i8 %res
@llvm
def __gt__(self: int, b: float) -> bool:
%0 = sitofp i64 %self to double
%1 = fcmp ogt double %0, %b
%2 = zext i1 %1 to i8
ret i8 %2
@llvm
def __gt__(a: int, b: int) -> bool:
%tmp = icmp sgt i64 %a, %b
%res = zext i1 %tmp to i8
ret i8 %res
@llvm
def __le__(self: int, b: float) -> bool:
%0 = sitofp i64 %self to double
%1 = fcmp ole double %0, %b
%2 = zext i1 %1 to i8
ret i8 %2
@llvm
def __le__(a: int, b: int) -> bool:
%tmp = icmp sle i64 %a, %b
%res = zext i1 %tmp to i8
ret i8 %res
@llvm
def __ge__(self: int, b: float) -> bool:
%0 = sitofp i64 %self to double
%1 = fcmp oge double %0, %b
%2 = zext i1 %1 to i8
ret i8 %2
@llvm
def __ge__(a: int, b: int) -> bool:
%tmp = icmp sge i64 %a, %b
%res = zext i1 %tmp to i8
ret i8 %res
def __pow__(self: int, exp: float):
return float(self) ** exp
def __pow__(self: int, exp: int):
if exp < 0:
return 0
result = 1
while True:
if exp & 1:
result *= self
exp >>= 1
if not exp:
break
self *= self
return result
@extend
class int:
def __int__(self) -> int:
global OP_COUNT
OP_COUNT = inc(OP_COUNT)
return self
def __float__(self) -> float:
global OP_COUNT
OP_COUNT = inc(OP_COUNT)
return I.__float__(self)
def __bool__(self) -> bool:
global OP_COUNT
OP_COUNT = inc(OP_COUNT)
return I.__bool__(self)
def __pos__(self) -> int:
global OP_COUNT
OP_COUNT = inc(OP_COUNT)
return self
def __neg__(self) -> int:
global OP_COUNT
OP_COUNT = inc(OP_COUNT)
return I.__neg__(self)
def __lshift__(self, other: int) -> int:
global OP_COUNT
OP_COUNT = inc(OP_COUNT)
return I.__lshift__(self, other)
def __rshift__(self, other: int) -> int:
global OP_COUNT
OP_COUNT = inc(OP_COUNT)
return I.__rshift__(self, other)
def __add__(self, other: float) -> float:
global OP_COUNT
OP_COUNT = inc(OP_COUNT)
return I.__add__(self, other)
def __add__(self, b: int) -> int:
global OP_COUNT
OP_COUNT = inc(OP_COUNT)
return I.__add__(self, b)
def __sub__(self, other: float) -> float:
global OP_COUNT
OP_COUNT = inc(OP_COUNT)
return I.__sub__(self, other)
def __sub__(self, b: int) -> int:
global OP_COUNT
OP_COUNT = inc(OP_COUNT)
return I.__sub__(self, b)
def __mul__(self, other: float) -> float:
global OP_COUNT
OP_COUNT = inc(OP_COUNT)
return I.__mul__(self, other)
def __mul__(self, b: int) -> int:
global OP_COUNT
OP_COUNT = inc(OP_COUNT)
return I.__mul__(self, b)
def __floordiv__(self, other: float) -> float:
global OP_COUNT
OP_COUNT = inc(OP_COUNT)
return I.__floordiv__(self, other)
def __floordiv__(self, b: int) -> int:
global OP_COUNT
OP_COUNT = inc(OP_COUNT)
return I.__floordiv__(self, b)
def __truediv__(self, other: float) -> float:
global OP_COUNT
OP_COUNT = inc(OP_COUNT)
return I.__truediv__(self, other)
def __truediv__(self, other: int) -> float:
global OP_COUNT
OP_COUNT = inc(OP_COUNT)
return I.__truediv__(self, other)
def __mod__(self, other: float) -> float:
global OP_COUNT
OP_COUNT = inc(OP_COUNT)
return I.__mod__(self, other)
def __mod__(self, b: int) -> int:
global OP_COUNT
OP_COUNT = inc(OP_COUNT)
return I.__mod__(self, b)
def __invert__(self) -> int:
global OP_COUNT
OP_COUNT = inc(OP_COUNT)
return I.__invert__(self)
def __and__(self, b: int) -> int:
global OP_COUNT
OP_COUNT = inc(OP_COUNT)
return I.__and__(self, b)
def __or__(self, b: int) -> int:
global OP_COUNT
OP_COUNT = inc(OP_COUNT)
return I.__or__(self, b)
def __xor__(self, b: int) -> int:
global OP_COUNT
OP_COUNT = inc(OP_COUNT)
return I.__xor__(self, b)
def __eq__(self, b: float) -> bool:
global OP_COUNT
OP_COUNT = inc(OP_COUNT)
return I.__eq__(self, b)
def __eq__(self, b: int) -> bool:
global OP_COUNT
OP_COUNT = inc(OP_COUNT)
return I.__eq__(self, b)
def __ne__(self, b: float) -> bool:
global OP_COUNT
OP_COUNT = inc(OP_COUNT)
return I.__ne__(self, b)
def __ne__(self, b: int) -> bool:
global OP_COUNT
OP_COUNT = inc(OP_COUNT)
return I.__ne__(self, b)
def __lt__(self, b: float) -> bool:
global OP_COUNT
OP_COUNT = inc(OP_COUNT)
return I.__lt__(self, b)
def __lt__(self, b: int) -> bool:
global OP_COUNT
OP_COUNT = inc(OP_COUNT)
return I.__lt__(self, b)
def __gt__(self, b: float) -> bool:
global OP_COUNT
OP_COUNT = inc(OP_COUNT)
return I.__gt__(self, b)
def __gt__(self, b: int) -> bool:
global OP_COUNT
OP_COUNT = inc(OP_COUNT)
return I.__gt__(self, b)
def __le__(self, b: float) -> bool:
global OP_COUNT
OP_COUNT = inc(OP_COUNT)
return I.__le__(self, b)
def __le__(self, b: int) -> bool:
global OP_COUNT
OP_COUNT = inc(OP_COUNT)
return I.__le__(self, b)
def __ge__(self, b: float) -> bool:
global OP_COUNT
OP_COUNT = inc(OP_COUNT)
return I.__ge__(self, b)
def __ge__(self, b: int) -> bool:
global OP_COUNT
OP_COUNT = inc(OP_COUNT)
return I.__ge__(self, b)
def __pow__(self, exp: float):
global OP_COUNT
OP_COUNT = inc(OP_COUNT)
return I.__pow__(self, exp)
def __pow__(self, exp: int):
global OP_COUNT
OP_COUNT = inc(OP_COUNT)
return I.__pow__(self, exp)
class F:
@llvm
def __int__(self: float) -> int:
%0 = fptosi double %self to i64
ret i64 %0
def __float__(self: float):
return self
@llvm
def __bool__(self: float) -> bool:
%0 = fcmp one double %self, 0.000000e+00
%1 = zext i1 %0 to i8
ret i8 %1
@extend
class float:
def __int__(self) -> int:
global OP_COUNT
OP_COUNT = inc(OP_COUNT)
return F.__int__(self)
def __float__(self) -> float:
global OP_COUNT
OP_COUNT = inc(OP_COUNT)
return self
def __bool__(self) -> bool:
global OP_COUNT
OP_COUNT = inc(OP_COUNT)
return F.__bool__(self)
@extend
class bool:
def __int__(self) -> int:
global OP_COUNT
OP_COUNT = inc(OP_COUNT)
return 1 if self else 0
def __float__(self) -> float:
global OP_COUNT
OP_COUNT = inc(OP_COUNT)
return 1. if self else 0.
def __bool__(self) -> bool:
global OP_COUNT
OP_COUNT = inc(OP_COUNT)
return self
def eq(a: int, b: int) -> bool:
return I.__eq__(a, b)
@noinline
def foo(x):
return x
@test
def test_int_simple_fold():
op_count = OP_COUNT
x = 1
y = x + 2
z = x + 3
assert eq(foo(x + 1), 2)
assert eq(foo(y * 2), 6)
assert eq(foo(z // 3), 1)
assert eq(foo(x >> 2), 0)
assert eq(foo(x << y), 8)
assert eq(foo(x | y), 3)
assert eq(foo(z & z), 4)
assert not foo(x > y)
assert foo(y < z)
assert not foo(x >= z)
assert foo(x <= 2)
assert foo(x == 1)
assert foo(x != 2)
assert eq(foo(y ** 3), 27)
assert eq(foo(y ** 0), 1)
assert eq(foo(y ** -1), 0)
assert eq(op_count, OP_COUNT)
test_int_simple_fold()
@test
def test_ternary_fold():
op_count = OP_COUNT
x = 1
y = x * 2
assert (x + 1 if x != 0 else -1) > 0
assert (x + 1 if x == 0 else -1) < 0
assert eq(op_count, OP_COUNT)
test_ternary_fold()
@test
def test_try_catch_fold():
op_count = OP_COUNT
x = 0
y = x + 1
try:
x = 1
finally:
x = 4
assert x == 4
assert eq(op_count, OP_COUNT)
test_try_catch_fold()
@test
def test_while_fold():
op_count = OP_COUNT
x = 0
y = 1
while (x != 0):
y = 2
assert y + 1 == 2
assert eq(op_count, OP_COUNT)
test_while_fold()
@test
def test_imperative_for_fold():
op_count = OP_COUNT
foo = 2
y = 1
for i in range(foo, 2):
y = foo - 1
assert y == 1
assert eq(op_count, OP_COUNT)
test_imperative_for_fold()
@test
def test_long_fold():
op_count = OP_COUNT
x = 3
assert eq(foo(x + x + x + x + x + x + x + x + x + x + x + x), 36)
assert eq(op_count, OP_COUNT)
test_long_fold()
@test
def test_conversions():
op_count = OP_COUNT
n = 42
b = True
x = 3.14
assert eq(foo(n.__int__()), n)
assert foo(n.__float__()) == 42.0
assert foo(n.__bool__())
assert eq(foo(b.__int__()), 1)
assert foo(b.__float__()) == 1.0
assert foo(b.__bool__())
assert eq(foo(x.__int__()), 3)
assert foo(x.__float__()) == x
assert foo(x.__bool__())
assert eq(op_count, OP_COUNT)
test_conversions()
@test
def test_no_ops():
def v(): return 42
op_count = OP_COUNT
n = v()
assert eq(+n, n)
assert eq(-(-n), n)
assert eq(~(~n), n)
assert eq(op_count, OP_COUNT)
test_no_ops()
@test
def test_algebraic_simplification():
def v(): return 42
op_count = OP_COUNT
n = v()
assert eq(0*n, 0)
assert eq(n*0, 0)
assert eq(n*1, n)
assert eq(1*n, n)
assert eq(n+0, n)
assert eq(0+n, n)
assert eq(n-0, n)
assert eq(0//n, 0)
assert eq(op_count, OP_COUNT)
test_algebraic_simplification()
n = 42
@test
def test_global_fold():
op_count = OP_COUNT
m = 1
assert eq(n + m, 43)
assert eq(op_count, OP_COUNT)
test_global_fold()
# make sure globals fold properly with demotion
op_count1 = OP_COUNT
a = foo(42)
a = 42
b = a + 10
c = b
op_count2 = OP_COUNT
@test
def test_global_fold_non_const():
op_count = OP_COUNT
assert c == 52
assert op_count1 == op_count2
test_global_fold_non_const()
some_global = 42
@test
def test_side_effect_analysis():
@pure
@test
def foo():
assert False
return some_global
def bar():
a = 1
b = 2
c = 3
return foo()
def baz():
global some_global
some_global += 1
return some_global
def fab():
return baz()
foo()
x = foo()
bar()
y = bar()
assert baz() == 43
baz()
assert some_global == 44
assert fab() == 45
fab()
assert some_global == 46
test_side_effect_analysis()