mirror of https://github.com/exaloop/codon.git
695 lines
16 KiB
Python
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()
|