Skip to content

Commit d86b8d5

Browse files
committed
Allow inlinable LLVM function definitions.
1 parent 4344db8 commit d86b8d5

File tree

5 files changed

+65
-6
lines changed

5 files changed

+65
-6
lines changed

typed_python/compiler/native_compiler/native_ast.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,8 @@ def const_str(c):
208208
external=bool,
209209
varargs=bool,
210210
intrinsic=bool,
211-
can_throw=bool
211+
can_throw=bool,
212+
inlineLlvmDefinition=OneOf(None, str)
212213
)
213214

214215

typed_python/compiler/native_compiler/native_ast_to_llvm.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
import typed_python.compiler.native_compiler.native_ast as native_ast
2626
import llvmlite.ir
27+
import re
2728
import os
2829

2930

@@ -293,6 +294,7 @@ def add_functions(self, names_to_definitions):
293294

294295
globalDefinitions = {}
295296
globalDefinitionsLlvmValues = {}
297+
extraDefinitions = {}
296298

297299
while names_to_definitions:
298300
for name in sorted(names_to_definitions):
@@ -322,7 +324,8 @@ def add_functions(self, names_to_definitions):
322324
builder,
323325
arg_assignments,
324326
definition.output_type,
325-
external_function_references
327+
external_function_references,
328+
extraDefinitions
326329
)
327330

328331
func_converter.setup()
@@ -375,8 +378,21 @@ def add_functions(self, names_to_definitions):
375378
if name not in self._function_definitions:
376379
raise Exception(f"Somehow we depend on {name} but have no definition for it")
377380

381+
moduleText = str(module)
382+
383+
# substitute in direct LLVM IR function definitions for the 'extraDefinitins'
384+
# llvmlite produces 'declare' stubs for these functions which we need to remove
385+
# so that we can place a direct declaration. This is obviously very fragile but
386+
# gives us an ability to work around llvmlite not having total coverage for all
387+
# llvm features we might want (such as vectors).
388+
for fName, fDef in extraDefinitions.items():
389+
moduleText = moduleText + "\n\n" + fDef
390+
391+
pattern = '\ndeclare .*@"' + fName + '"\\([^)]*\\)'
392+
moduleText = re.sub(pattern, '', moduleText)
393+
378394
return ModuleDefinition(
379-
str(module),
395+
moduleText,
380396
functionTypes,
381397
globalDefinitions,
382398
usedExternalFunctions,

typed_python/compiler/native_compiler/native_ast_to_llvm_function_converter.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -567,7 +567,8 @@ def __init__(self,
567567
builder,
568568
arg_assignments,
569569
output_type,
570-
external_function_references
570+
external_function_references,
571+
extraDefinitions
571572
):
572573
self.function = function
573574

@@ -583,6 +584,7 @@ def __init__(self,
583584
self.external_function_references = external_function_references
584585
self.tags_initialized = {}
585586
self.stack_slots = {}
587+
self.extraDefinitions = extraDefinitions
586588

587589
def tags_as(self, new_tags):
588590
class scoper():
@@ -694,6 +696,9 @@ def generate_exception_and_store_value(self, llvm_pointer_val):
694696

695697
def namedCallTargetToLLVM(self, target):
696698
if target.external:
699+
if target.inlineLlvmDefinition:
700+
self.extraDefinitions[target.name] = target.inlineLlvmDefinition
701+
697702
if target.name not in self.external_function_references:
698703
func_type = llvmlite.ir.FunctionType(
699704
type_to_llvm_type(target.output_type),
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
from typed_python import Entrypoint
2+
from typed_python.compiler.type_wrappers.compilable_builtin import CompilableBuiltin
3+
from typed_python.compiler.type_wrappers.runtime_functions import externalCallTarget, Float64
4+
5+
6+
inlineLlvmFunc = externalCallTarget("inlineLlvmFunc", Float64, Float64, inlineLlvmDefinition="""
7+
define external double @"inlineLlvmFunc"(double %".1") {
8+
entry:
9+
%.4 = fadd double %.1, 1.000000e+00
10+
ret double %.4
11+
}
12+
""")
13+
14+
15+
class InlineLlvmFunc(CompilableBuiltin):
16+
def __eq__(self, other):
17+
return isinstance(other, inlineLlvmFunc)
18+
19+
def __hash__(self):
20+
return hash("inlineLlvmFunc")
21+
22+
def convert_call(self, context, instance, args, kwargs):
23+
return context.pushPod(
24+
float,
25+
inlineLlvmFunc.call(
26+
args[0],
27+
)
28+
)
29+
30+
31+
def test_inline_llvm():
32+
@Entrypoint
33+
def f(x):
34+
return InlineLlvmFunc()(x)
35+
36+
assert f(2.5) == 3.5

typed_python/compiler/type_wrappers/runtime_functions.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
Void = native_ast.Void
3232

3333

34-
def externalCallTarget(name, output, *inputs, varargs=False, intrinsic=False, canThrow=False):
34+
def externalCallTarget(name, output, *inputs, varargs=False, intrinsic=False, canThrow=False, inlineLlvmDefinition=None):
3535
"""Create an object that lets us call C functions.
3636
3737
Note that 'canThrow' really indicates whether we should use llvm 'invoke' instead
@@ -46,7 +46,8 @@ def externalCallTarget(name, output, *inputs, varargs=False, intrinsic=False, ca
4646
external=True,
4747
varargs=varargs,
4848
intrinsic=intrinsic,
49-
can_throw=canThrow
49+
can_throw=canThrow,
50+
inlineLlvmDefinition=inlineLlvmDefinition
5051
)
5152
)
5253

0 commit comments

Comments
 (0)