Skip to content

Commit 196da9e

Browse files
author
William Grant
committed
Add print functions for native and llvm IR.
Given a compiled Function, this looks for a matching function in the converter and pulls the string representation, throwing if not found.
1 parent 5322a88 commit 196da9e

File tree

2 files changed

+202
-0
lines changed

2 files changed

+202
-0
lines changed

typed_python/compiler/introspect.py

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
# Copyright 2017-2019 typed_python Authors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""introspect.py
16+
17+
Contains helper functions for exploring the objects and syntax trees generatied in the process
18+
of going from python -> native IR -> LLVM IR.
19+
"""
20+
21+
import typed_python
22+
import typed_python.compiler.python_to_native_converter as python_to_native_converter
23+
24+
from typed_python import Runtime, Function
25+
26+
27+
def getNativeIRString(
28+
typedFunc: Function, args=None, kwargs=None
29+
) -> str:
30+
"""
31+
Given a function compiled with Entrypoint, return a text representation
32+
of the generated native (one layer prior to LLVM) code.
33+
34+
Args:
35+
typedFunc (Function): a decorated python function.
36+
args (Optional(list)): these optional args should be the Types of the functions' positional arguments
37+
kwargs (Optional(dict)): these keyword args should be the Types of the functions' keyword arguments
38+
39+
Returns:
40+
A string for the function bodies generated (including constructors and destructors)
41+
"""
42+
converter = Runtime.singleton().llvm_compiler.converter
43+
44+
function_name = getFullFunctionNameWithArgs(typedFunc, args, kwargs)
45+
# relies on us maintaining our naming conventions (tests would break otherwise)
46+
output_str = ""
47+
for key, value in converter._function_definitions.items():
48+
if function_name in key:
49+
output_str += f"Function {key}" + "_" * 20 + "\n"
50+
output_str += str(value.body.body) + "\n"
51+
output_str += "_" * 80 + "\n"
52+
53+
if not output_str:
54+
raise ValueError(
55+
"no matching function definitions found - has the code been compiled (and run)?"
56+
)
57+
58+
return output_str
59+
60+
61+
def getLLVMString(
62+
typedFunc: Function, args=None, kwargs=None
63+
) -> str:
64+
"""
65+
Given a function compiled with Entrypoint, return a text representation
66+
of the generated LLVM code.
67+
68+
Args:
69+
typedFunc (Function): a decorated python function.
70+
args (Optional(list)): these optional args should be the Types of the functions' positional arguments
71+
kwargs (Optional(dict)): these keyword args should be the Types of the functions' keyword arguments
72+
73+
Returns:
74+
A string for the function bodies generated (including constructors and destructors)
75+
"""
76+
converter = Runtime.singleton().llvm_compiler.converter
77+
78+
function_name = getFullFunctionNameWithArgs(typedFunc, args, kwargs)
79+
80+
output_str = ""
81+
for key, value in converter._functions_by_name.items():
82+
if function_name in key:
83+
output_str += f"Function {key}" + "_" * 20 + "\n"
84+
output_str += str(value) + "\n"
85+
output_str += "_" * 80 + "\n"
86+
87+
return output_str
88+
89+
90+
def getFullFunctionNameWithArgs(funcObj, argTypes, kwargTypes):
91+
"""
92+
Given a Function and a set of types, compile the function to generate the unique name
93+
for that function+argument combination.
94+
95+
Args:
96+
funcObj (Function): a typed_python Function.
97+
argTypes (List): a list of the position arguments for the function.
98+
kwargTypes (Dict): a key:value mapping for the functions' keywords arguments.
99+
"""
100+
assert isinstance(funcObj, typed_python._types.Function)
101+
typeWrapper = lambda t: python_to_native_converter.typedPythonTypeToTypeWrapper(t)
102+
funcObj = typed_python._types.prepareArgumentToBePassedToCompiler(funcObj)
103+
argTypes = [typeWrapper(a) for a in argTypes] if argTypes is not None else []
104+
kwargTypes = (
105+
{k: typeWrapper(v) for k, v in kwargTypes.items()}
106+
if kwargTypes is not None
107+
else {}
108+
)
109+
110+
overload_index = 0
111+
overload = funcObj.overloads[overload_index]
112+
113+
ExpressionConversionContext = (
114+
typed_python.compiler.expression_conversion_context.ExpressionConversionContext
115+
)
116+
argumentSignature = (
117+
ExpressionConversionContext.computeFunctionArgumentTypeSignature(
118+
overload, argTypes, kwargTypes
119+
)
120+
)
121+
122+
if argumentSignature is not None:
123+
callTarget = (
124+
Runtime()
125+
.singleton()
126+
.compileFunctionOverload(
127+
funcObj, overload_index, argumentSignature, argumentsAreTypes=True
128+
)
129+
)
130+
return callTarget.name
131+
else:
132+
raise ValueError("no signature found.")
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# Copyright 2017-2019 typed_python Authors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import pytest
16+
import unittest
17+
18+
from typed_python import Entrypoint, ListOf
19+
from typed_python.compiler import introspect
20+
21+
22+
class TestRuntime(unittest.TestCase):
23+
24+
# the compilation itself is handled in other tests.
25+
def naive_sum(someList, startingInt):
26+
for x in someList:
27+
startingInt += x
28+
return startingInt
29+
30+
compiled = Entrypoint(naive_sum)
31+
32+
def test_ir_compile_and_output_string(self):
33+
for test_func in [introspect.getNativeIRString, introspect.getLLVMString]:
34+
output_text = test_func(
35+
TestRuntime.compiled, args=[ListOf(int), int], kwargs=None
36+
)
37+
assert 'naive_sum' in output_text
38+
39+
def test_ir_throw_error_if_uncompiled(self):
40+
for test_func in [introspect.getNativeIRString, introspect.getLLVMString]:
41+
with pytest.raises(AssertionError):
42+
_ = test_func(
43+
TestRuntime.naive_sum, args=[ListOf(int), int], kwargs=None)
44+
45+
def test_introspect_handles_tp_class(self):
46+
"""Full TP class"""
47+
pass
48+
49+
def test_introspect_distinguishes_overloads(self):
50+
"""Check that we can obtain the correct IRs given multiple overloads."""
51+
for test_func in [introspect.getNativeIRString, introspect.getLLVMString]:
52+
overload_one = test_func(
53+
TestRuntime.compiled, args=[ListOf(int), int], kwargs=None)
54+
overload_two = test_func(
55+
TestRuntime.compiled, args=[ListOf(float), float], kwargs=None)
56+
assert overload_one != overload_two
57+
58+
def test_introspect_handles_kwargs_correctly_(self):
59+
for test_func in [introspect.getNativeIRString, introspect.getLLVMString]:
60+
output_text = test_func(
61+
TestRuntime.compiled, args=[ListOf(int)], kwargs={'startingInt': int}
62+
)
63+
assert 'naive_sum' in output_text
64+
65+
def test_introspect_rejects_invalid_args(self):
66+
for test_func in [introspect.getNativeIRString, introspect.getLLVMString]:
67+
with pytest.raises(ValueError):
68+
_ = test_func(TestRuntime.compiled, args=[ListOf(int), int, int])
69+
with pytest.raises(ValueError):
70+
_ = test_func(TestRuntime.compiled, args=[ListOf(int)], kwargs={'test': int})

0 commit comments

Comments
 (0)