-
Notifications
You must be signed in to change notification settings - Fork 1.1k
/
Copy pathexternals.py
155 lines (123 loc) · 4.74 KB
/
externals.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
"""
Register external C functions necessary for Numba code generation.
"""
import sys
from llvmlite import ir
import llvmlite.binding as ll
from numba.core import utils, intrinsics
from numba import _helperlib
def _add_missing_symbol(symbol, addr):
"""Add missing symbol into LLVM internal symtab
"""
if not ll.address_of_symbol(symbol):
ll.add_symbol(symbol, addr)
def _get_msvcrt_symbol(symbol):
"""
Under Windows, look up a symbol inside the C runtime
and return the raw pointer value as an integer.
"""
from ctypes import cdll, cast, c_void_p
f = getattr(cdll.msvcrt, symbol)
return cast(f, c_void_p).value
def compile_multi3(context):
"""
Compile the multi3() helper function used by LLVM
for 128-bit multiplication on 32-bit platforms.
"""
codegen = context.codegen()
library = codegen.create_library("multi3")
ir_mod = library.create_ir_module("multi3")
i64 = ir.IntType(64)
i128 = ir.IntType(128)
lower_mask = ir.Constant(i64, 0xffffffff)
_32 = ir.Constant(i64, 32)
_64 = ir.Constant(i128, 64)
fn_type = ir.FunctionType(i128, [i128, i128])
fn = ir.Function(ir_mod, fn_type, name="multi3")
a, b = fn.args
bb = fn.append_basic_block()
builder = ir.IRBuilder(bb)
# This implementation mimics compiler-rt's.
al = builder.trunc(a, i64)
bl = builder.trunc(b, i64)
ah = builder.trunc(builder.ashr(a, _64), i64)
bh = builder.trunc(builder.ashr(b, _64), i64)
# Compute {rh, rl} = al * bl (unsigned 64-bit multiplication)
# rl = (al & 0xffffffff) * (bl & 0xffffffff)
rl = builder.mul(builder.and_(al, lower_mask), builder.and_(bl, lower_mask))
# t = rl >> 32
t = builder.lshr(rl, _32)
# rl &= 0xffffffff
rl = builder.and_(rl, lower_mask)
# t += (al >> 32) * (bl & 0xffffffff)
t = builder.add(t, builder.mul(builder.lshr(al, _32),
builder.and_(bl, lower_mask)))
# rl += t << 32
rl = builder.add(rl, builder.shl(t, _32))
# rh = t >> 32
rh = builder.lshr(t, _32)
# t = rl >> 32
t = builder.lshr(rl, _32)
# rl &= 0xffffffff
rl = builder.and_(rl, lower_mask)
# t += (bl >> 32) * (al & 0xffffffff)
t = builder.add(t, builder.mul(builder.lshr(bl, _32),
builder.and_(al, lower_mask)))
# rl += t << 32
rl = builder.add(rl, builder.shl(t, _32))
# rh += t >> 32
rh = builder.add(rh, builder.lshr(t, _32))
# rh += (al >> 32) * (bl >> 32)
rh = builder.add(rh, builder.mul(builder.lshr(al, _32),
builder.lshr(bl, _32)))
# rh += (bh * al) + (bl * ah)
rh = builder.add(rh, builder.mul(bh, al))
rh = builder.add(rh, builder.mul(bl, ah))
# r = rl + (rh << 64)
r = builder.zext(rl, i128)
r = builder.add(r, builder.shl(builder.zext(rh, i128), _64))
builder.ret(r)
library.add_ir_module(ir_mod)
library.finalize()
return library
class _Installer(object):
_installed = False
def install(self, context):
"""
Install the functions into LLVM. This only needs to be done once,
as the mappings are persistent during the process lifetime.
"""
if not self._installed:
self._do_install(context)
self._installed = True
class _ExternalMathFunctions(_Installer):
"""
Map the math functions from the C runtime library into the LLVM
execution environment.
"""
def _do_install(self, context):
is32bit = utils.MACHINE_BITS == 32
c_helpers = _helperlib.c_helpers
if sys.platform.startswith('win32') and is32bit:
# For Windows XP _ftol2 is not defined, we will just use
# _ftol as a replacement.
# On Windows 7, this is not necessary but will work anyway.
ftol = _get_msvcrt_symbol("_ftol")
_add_missing_symbol("_ftol2", ftol)
elif sys.platform.startswith('linux') and is32bit:
_add_missing_symbol("__fixunsdfdi", c_helpers["fptoui"])
_add_missing_symbol("__fixunssfdi", c_helpers["fptouif"])
if is32bit:
# Make the library immortal
self._multi3_lib = compile_multi3(context)
ptr = self._multi3_lib.get_pointer_to_function("multi3")
assert ptr
_add_missing_symbol("__multi3", ptr)
# List available C-math
for fname in intrinsics.INTR_MATH:
# Force binding from CPython's C runtime library.
# (under Windows, different versions of the C runtime can
# be loaded at the same time, for example msvcrt100 by
# CPython and msvcrt120 by LLVM)
ll.add_symbol(fname, c_helpers[fname])
c_math_functions = _ExternalMathFunctions()