diff --git a/CHANGELOG b/CHANGELOG index 5af6169..45d6a5f 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,40 @@ +2014-04-28 0.12.5: +--------------------- + * Fixes memory leaks (#92) + * Fixes tarball (#99) + +2014-03-20 0.12.4: +--------------------- + * Add dylib_import_library and friends + * Fix BasicBlock downcast + * Module hashing + * Fix test script + +2014-02-18 0.12.3: +--------------------- + * Fix deprecation message for py2.6 + * Fix llvm_cbuilder for using deprecated_alloca + * Merged PR #88 by cantora + * Merged PR #94 by cgohlke + +2014-02-04 0.12.2: +--------------------- + * enhance wrapper efficiency by moving some capsule code into C++ + * fix unclosed file handler in avx_support + * multiple-dimension insert_value, extract_value + * various minor fixes + +2013-11-11 0.12.1: +--------------------- + * various bug fixes + +2013-08-28 0.12.0: +--------------------- + * update to LLVM 3.3 and maintain compatibility with LLVM 3.2 + * add LLRT for minimal support for 64-bit divmod on 32-bit platform + * start to adopt MCJIT (not quite usable on win32) + * various bug fixes + 2013-03-05 0.11.1: -------------------- * fix test when cc is not available diff --git a/MANIFEST.in b/MANIFEST.in index 295794c..25a211d 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,5 +1,6 @@ -include CHANGELOG LICENSE README setup.py MANIFEST.in +include CHANGELOG LICENSE README.rst setup.py MANIFEST.in versioneer.py recursive-include llvm * +recursive-include llvmpy * recursive-include www * recursive-include test * recursive-include tools * diff --git a/README.rst b/README.rst index 0fbcd24..a1abf84 100644 --- a/README.rst +++ b/README.rst @@ -30,10 +30,13 @@ Quickstart to separate your custom build from the default system package. Please replace ``LLVM_INSTALL_PATH`` with your own path. -3. Run ``REQUIRES_RTTI=1 make`` to build. +3. Run ``REQUIRES_RTTI=1 make install`` to build and install. **Note**: With LLVM 3.2, the default build configuration has C++ RTTI disabled. However, llvmpy requires RTTI. + + **Note**: Use ``make -j2 install`` to enable concurrent build. + Replace ``2`` with the actual number of processor you have. 4. Get llvm-py and install it:: diff --git a/buildscripts/condarecipe/bld.bat b/buildscripts/condarecipe/bld.bat new file mode 100644 index 0000000..e9177c5 --- /dev/null +++ b/buildscripts/condarecipe/bld.bat @@ -0,0 +1,6 @@ +set LLVMPY_DYNLINK=0 +set INCLUDE=%LIBRARY_INC% +set LIBPATH=%LIBRARY_LIB% +set LIB=%LIBRARY_LIB% +%PYTHON% setup.py install +if errorlevel 1 exit 1 diff --git a/buildscripts/condarecipe/build.sh b/buildscripts/condarecipe/build.sh new file mode 100644 index 0000000..5c98fde --- /dev/null +++ b/buildscripts/condarecipe/build.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +if [[ (`uname` == Linux) && (`uname -m` != armv6l) ]] +then + export CC=gcc + #gcc44 + export CXX=g++ + #g++44 +fi + +export LLVMPY_DYNLINK=$DISTRO_BUILD + +$PYTHON setup.py install diff --git a/buildscripts/condarecipe/meta.yaml b/buildscripts/condarecipe/meta.yaml new file mode 100644 index 0000000..4612840 --- /dev/null +++ b/buildscripts/condarecipe/meta.yaml @@ -0,0 +1,28 @@ +package: + name: llvmpy + version: 99.9.9 + +source: + git_url: git@github.com:llvmpy/llvmpy.git +# git_tag: 0.12.0 + +requirements: + build: + - llvm + - python + #- chrpath [linux] + run: + - llvm [unix] + - python + +test: + imports: + - llvm + - llvmpy + - llvmpy._api + - llvmpy._capsule + - llpython + - llvm_array + - llvm_cbuilder + + diff --git a/buildscripts/condarecipe/run_test.py b/buildscripts/condarecipe/run_test.py new file mode 100644 index 0000000..682b8d4 --- /dev/null +++ b/buildscripts/condarecipe/run_test.py @@ -0,0 +1,22 @@ +import sys +import platform +import llvm + +from llvm.core import Module +from llvm.ee import EngineBuilder +from llvm.utils import check_intrinsics + +m = Module.new('fjoidajfa') +eb = EngineBuilder.new(m) +target = eb.select_target() + +print('target.triple=%r' % target.triple) +if sys.platform == 'darwin': + s = {'64bit': 'x86_64', '32bit': 'x86'}[platform.architecture()[0]] + assert target.triple.startswith(s + '-apple-darwin') + +assert llvm.test(verbosity=2, run_isolated=False) == 0 +#check_intrinsics.main() + +print('llvm.__version__: %s' % llvm.__version__) +#assert llvm.__version__ == '0.12.0' diff --git a/docs/source/index.rst b/docs/source/index.rst index 7798b77..fbf5c8b 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -3,8 +3,8 @@ You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. -Documentation for llvmpy -======================== +llvmpy +====== Contents: @@ -22,7 +22,7 @@ Contents: Indices and tables -================== +------------------ * :ref:`genindex` * :ref:`modindex` diff --git a/example/vector_instr.py b/example/vector_instr.py new file mode 100644 index 0000000..e49d06a --- /dev/null +++ b/example/vector_instr.py @@ -0,0 +1,152 @@ +''' +This example shows: +1) how to use vector instructions +2) how to take advantage of LLVM loop vectorization to transform scalar + operations to vector operations +''' + +from __future__ import print_function +import llvm.core as lc +import llvm.ee as le +import llvm.passes as lp +from ctypes import CFUNCTYPE, POINTER, c_int, c_float + +def build_manual_vector(): + mod = lc.Module.new('manual.vector') + intty = lc.Type.int(32) + vecty = lc.Type.vector(lc.Type.float(), 4) + aryty = lc.Type.pointer(lc.Type.float()) + fnty = lc.Type.function(lc.Type.void(), [aryty, aryty, aryty, intty]) + fn = mod.add_function(fnty, name='vector_add') + bbentry = fn.append_basic_block('entry') + bbloopcond = fn.append_basic_block('loop.cond') + bbloopbody = fn.append_basic_block('loop.body') + bbexit = fn.append_basic_block('exit') + builder = lc.Builder.new(bbentry) + + # populate function body + in1, in2, out, size = fn.args + ZERO = lc.Constant.null(intty) + loopi_ptr = builder.alloca(intty) + builder.store(ZERO, loopi_ptr) + + builder.branch(bbloopcond) + builder.position_at_end(bbloopcond) + + loopi = builder.load(loopi_ptr) + loopcond = builder.icmp(lc.ICMP_ULT, loopi, size) + + builder.cbranch(loopcond, bbloopbody, bbexit) + builder.position_at_end(bbloopbody) + + vecaryty = lc.Type.pointer(vecty) + in1asvec = builder.bitcast(builder.gep(in1, [loopi]), vecaryty) + in2asvec = builder.bitcast(builder.gep(in2, [loopi]), vecaryty) + outasvec = builder.bitcast(builder.gep(out, [loopi]), vecaryty) + + vec1 = builder.load(in1asvec) + vec2 = builder.load(in2asvec) + + vecout = builder.fadd(vec1, vec2) + + builder.store(vecout, outasvec) + + next = builder.add(loopi, lc.Constant.int(intty, 4)) + builder.store(next, loopi_ptr) + + builder.branch(bbloopcond) + builder.position_at_end(bbexit) + + builder.ret_void() + + return mod, fn + + +def build_auto_vector(): + mod = lc.Module.new('auto.vector') + # Loop vectorize is sensitive to the size of the index size(!?) + intty = lc.Type.int(tuple.__itemsize__ * 8) + aryty = lc.Type.pointer(lc.Type.float()) + fnty = lc.Type.function(lc.Type.void(), [aryty, aryty, aryty, intty]) + fn = mod.add_function(fnty, name='vector_add') + bbentry = fn.append_basic_block('entry') + bbloopcond = fn.append_basic_block('loop.cond') + bbloopbody = fn.append_basic_block('loop.body') + bbexit = fn.append_basic_block('exit') + builder = lc.Builder.new(bbentry) + + # populate function body + in1, in2, out, size = fn.args + in1.add_attribute(lc.ATTR_NO_ALIAS) + in2.add_attribute(lc.ATTR_NO_ALIAS) + out.add_attribute(lc.ATTR_NO_ALIAS) + ZERO = lc.Constant.null(intty) + loopi_ptr = builder.alloca(intty) + builder.store(ZERO, loopi_ptr) + + builder.branch(bbloopcond) + builder.position_at_end(bbloopcond) + + loopi = builder.load(loopi_ptr) + loopcond = builder.icmp(lc.ICMP_ULT, loopi, size) + + builder.cbranch(loopcond, bbloopbody, bbexit) + builder.position_at_end(bbloopbody) + + in1elem = builder.load(builder.gep(in1, [loopi])) + in2elem = builder.load(builder.gep(in2, [loopi])) + + outelem = builder.fadd(in1elem, in2elem) + + builder.store(outelem, builder.gep(out, [loopi])) + + next = builder.add(loopi, lc.Constant.int(intty, 1)) + builder.store(next, loopi_ptr) + + builder.branch(bbloopcond) + builder.position_at_end(bbexit) + + builder.ret_void() + + return mod, fn + +def example(title, module_builder, opt): + print(title.center(80, '=')) + mod, fn = module_builder() + + eb = le.EngineBuilder.new(mod).opt(3) + if opt: + print('opt') + tm = eb.select_target() + pms = lp.build_pass_managers(mod=mod, tm=tm, opt=3, loop_vectorize=True, + fpm=False) + pms.pm.run(mod) + + print(mod) + print(mod.to_native_assembly()) + + engine = eb.create() + ptr = engine.get_pointer_to_function(fn) + + callable = CFUNCTYPE(None, POINTER(c_float), POINTER(c_float), + POINTER(c_float), c_int)(ptr) + + N = 20 + in1 = (c_float * N)(*range(N)) + in2 = (c_float * N)(*range(N)) + out = (c_float * N)() + + print('in1: ', list(in1)) + print('in1: ', list(in2)) + + callable(in1, in2, out, N) + + print('out', list(out)) + + +def main(): + example('manual vector function', build_manual_vector, False) + example('auto vector function', build_auto_vector, True) + +if __name__ == '__main__': + main() diff --git a/llpython/byte_control.py b/llpython/byte_control.py index acd5a41..8ca407e 100644 --- a/llpython/byte_control.py +++ b/llpython/byte_control.py @@ -1,4 +1,3 @@ -#! /usr/bin/env python # ______________________________________________________________________ from __future__ import absolute_import import opcode diff --git a/llpython/byte_flow.py b/llpython/byte_flow.py index 2201791..197c839 100644 --- a/llpython/byte_flow.py +++ b/llpython/byte_flow.py @@ -1,4 +1,3 @@ -#! /usr/bin/env python # ______________________________________________________________________ from __future__ import absolute_import import dis diff --git a/llpython/byte_translator.py b/llpython/byte_translator.py index 9b6d2de..a6e5864 100644 --- a/llpython/byte_translator.py +++ b/llpython/byte_translator.py @@ -1,4 +1,3 @@ -#! /usr/bin/env python # ______________________________________________________________________ '''Defines a bytecode based LLVM translator for llpython code. ''' diff --git a/llpython/bytecode_visitor.py b/llpython/bytecode_visitor.py index 009d1a0..3fa3f48 100644 --- a/llpython/bytecode_visitor.py +++ b/llpython/bytecode_visitor.py @@ -1,4 +1,3 @@ -#! /usr/bin/env python # ______________________________________________________________________ from __future__ import absolute_import import itertools diff --git a/llpython/bytetype.py b/llpython/bytetype.py index 211f3f8..484d09e 100644 --- a/llpython/bytetype.py +++ b/llpython/bytetype.py @@ -1,4 +1,3 @@ -#! /usr/bin/env python # ______________________________________________________________________ import ctypes diff --git a/llpython/control_flow.py b/llpython/control_flow.py index 5ea030a..8026aba 100644 --- a/llpython/control_flow.py +++ b/llpython/control_flow.py @@ -1,4 +1,3 @@ -#! /usr/bin/env python # ______________________________________________________________________ import pprint diff --git a/llpython/gen_bytecode_visitor.py b/llpython/gen_bytecode_visitor.py index b29bc56..a60b345 100644 --- a/llpython/gen_bytecode_visitor.py +++ b/llpython/gen_bytecode_visitor.py @@ -1,4 +1,3 @@ -#! /usr/bin/env python # ______________________________________________________________________ from __future__ import absolute_import from . import opcode_util diff --git a/llpython/nobitey.py b/llpython/nobitey.py index 68bde5f..4236d29 100644 --- a/llpython/nobitey.py +++ b/llpython/nobitey.py @@ -1,4 +1,3 @@ -#! /usr/bin/env python # ______________________________________________________________________ from __future__ import absolute_import import sys diff --git a/llpython/opcode_util.py b/llpython/opcode_util.py index efd0c5c..7a1972b 100644 --- a/llpython/opcode_util.py +++ b/llpython/opcode_util.py @@ -1,4 +1,3 @@ -#! /usr/bin/env python # ______________________________________________________________________ import dis diff --git a/llpython/phi_injector.py b/llpython/phi_injector.py index ee28ab5..dfa1061 100644 --- a/llpython/phi_injector.py +++ b/llpython/phi_injector.py @@ -1,4 +1,3 @@ -#! /usr/bin/env python # ______________________________________________________________________ from .bytecode_visitor import BytecodeFlowVisitor, BenignBytecodeVisitorMixin diff --git a/llpython/pyaddfunc.py b/llpython/pyaddfunc.py index 8e09839..d6a2945 100644 --- a/llpython/pyaddfunc.py +++ b/llpython/pyaddfunc.py @@ -1,4 +1,3 @@ -#! /usr/bin/env python # ______________________________________________________________________ import ctypes diff --git a/llpython/tests/llfuncs.py b/llpython/tests/llfuncs.py index 2a8614f..8b5b1f3 100644 --- a/llpython/tests/llfuncs.py +++ b/llpython/tests/llfuncs.py @@ -1,4 +1,3 @@ -#! /usr/bin/env python # ______________________________________________________________________ def doslice (in_string, lower, upper): diff --git a/llpython/tests/llfunctys.py b/llpython/tests/llfunctys.py index b552cf4..8335044 100644 --- a/llpython/tests/llfunctys.py +++ b/llpython/tests/llfunctys.py @@ -1,4 +1,3 @@ -#! /usr/bin/env python # ______________________________________________________________________ import llvm.core as lc diff --git a/llrtc/Makefile b/llrtc/Makefile new file mode 100644 index 0000000..b15eac5 --- /dev/null +++ b/llrtc/Makefile @@ -0,0 +1,22 @@ +all: + make -C lib + +ir: + make -C lib ir + +test: + make -C lib test + +clean-test: + make -C lib clean-test + +clean-temp: + make -C lib clean-temp + +clean: + make -C lib clean + +install: ir + cp llrt_*.ll ../llvm/llrt + make -C lib clean-temp + \ No newline at end of file diff --git a/llrtc/README.md b/llrtc/README.md new file mode 100644 index 0000000..539d9c3 --- /dev/null +++ b/llrtc/README.md @@ -0,0 +1,25 @@ +# LLRT: Low Level Runtime + +## Why? + +The same reason for LLVM compiler-rt. LLVM generates libgcc symbols, such as +__divdi3 for 64-bit division on 32-bit platform. They are not also available. +We need to ship compiler-rt but it is not Windows ready. +This subproject aims to provide a small portable subset of compiler-rt. +Start small and add only the things we really needed. +Performance is not crucial but should not be terrible. +Functionality and usefullness should be more important than performance. + +## Developer Instructions + +LLRT implements some functionalities in compiler-rt in ANSI C. +The C files are compiled using clang to produce LLVM IR which are shipped. +The IR files are committed in the repository. +So, remember to build the IR files commit them after modifying the C files. + +## Build Requirement + +- Make +- Clang +- Python + diff --git a/llrtc/lib/.gitignore b/llrtc/lib/.gitignore new file mode 100644 index 0000000..fbcf76a --- /dev/null +++ b/llrtc/lib/.gitignore @@ -0,0 +1,4 @@ +*.o +*.run +*.out +*.ll diff --git a/llrtc/lib/Makefile b/llrtc/lib/Makefile new file mode 100644 index 0000000..9dbfb27 --- /dev/null +++ b/llrtc/lib/Makefile @@ -0,0 +1,66 @@ +OUTPUT = llrt +SOURCES = udivmod64.c sdivmod64.c div64.c mod64.c +TESTS = test_udivmod64.c test_sdivmod64.c + +CLANG = clang +LLVM_LINK = llvm-link +CF = -Wall -ansi +CF_TEST = $(CF) -ftrapv +CF_BUILD = $(CF) -O0 -emit-llvm +OUTDIR = .. +STRIPPER = ../tools/striptriple.py + +all: ir + +ir: $(OUTDIR)/$(OUTPUT)_x86.ll $(OUTDIR)/$(OUTPUT)_x86_64.ll + +$(OUTDIR)/$(OUTPUT)_x86.ll: $(SOURCES:.c=_x86.bc) + $(LLVM_LINK) -S $+ -o $@ + python $(STRIPPER) $@ + +$(OUTDIR)/$(OUTPUT)_x86_64.ll: $(SOURCES:.c=_x86_64.bc) + $(LLVM_LINK) -S $+ -o $@ + python $(STRIPPER) $@ + +build-test: $(SOURCES:.c=.o) $(TESTS:.c=.run) + +lib$(OUTPUT).a: $(SOURCES:.c=.o) + $(CLANG) -static $+ -o $@ + +test: $(TESTS:.c=.run) + for src in $+; do \ + echo "testing $${src}"; \ + python $${src%.*}.py > $${src%.*}.out; \ + done; + +clean-test: + rm -f *.out + rm -f *.o + rm -f *.run + +clean-dist: clean-temp + rm -f *.ll + +clean-temp: + rm -f *.bc + rm -f *.o + rm -f *.out + +clean: clean-test clean-dist + +%.c: llrt.h + +%_x86.bc: %.c + $(CLANG) -m32 $(CF_BUILD) -c $< -o $@ + +%_x86_64.bc: %.c + $(CLANG) -m64 $(CF_BUILD) -c $< -o $@ + +%.o: %.c + $(CLANG) $(CF_TEST) -c $< + +%.run: %.c + $(CLANG) $(CF_TEST) -o $@ $+ + +test_udivmod64.run: udivmod64.o +test_sdivmod64.run: udivmod64.o sdivmod64.o diff --git a/llrtc/lib/div64.c b/llrtc/lib/div64.c new file mode 100644 index 0000000..8466965 --- /dev/null +++ b/llrtc/lib/div64.c @@ -0,0 +1,11 @@ +#include "llrt.h" + +uint64_t udiv64(uint64_t dividend, uint64_t divisor) +{ + return udivmod64(dividend, divisor, NULL); +} + +int64_t sdiv64(int64_t dividend, int64_t divisor) +{ + return sdivmod64(dividend, divisor, NULL); +} diff --git a/llrtc/lib/llrt.h b/llrtc/lib/llrt.h new file mode 100644 index 0000000..4e9e348 --- /dev/null +++ b/llrtc/lib/llrt.h @@ -0,0 +1,19 @@ +#ifndef LLRT_H_ +#define LLRT_H_ + +#include + +#define NULL 0 +#define BITS_PER_BYTE 8 + +uint64_t udivmod64(uint64_t dividend, uint64_t divisor, uint64_t *remainder); +int64_t sdivmod64(int64_t dividend, int64_t divisor, int64_t *remainder); + +uint64_t udiv64(uint64_t dividend, uint64_t divisor); +int64_t sdiv64(int64_t dividend, int64_t divisor); + +uint64_t umod64(uint64_t dividend, uint64_t divisor); +int64_t smod64(int64_t dividend, int64_t divisor); + +#endif /* LLRT_H_ */ + diff --git a/llrtc/lib/mod64.c b/llrtc/lib/mod64.c new file mode 100644 index 0000000..875f6b9 --- /dev/null +++ b/llrtc/lib/mod64.c @@ -0,0 +1,15 @@ +#include "llrt.h" + +uint64_t umod64(uint64_t dividend, uint64_t divisor) +{ + uint64_t rem; + udivmod64(dividend, divisor, &rem); + return rem; +} + +int64_t smod64(int64_t dividend, int64_t divisor) +{ + int64_t rem; + sdivmod64(dividend, divisor, &rem); + return rem; +} diff --git a/llrtc/lib/sdivmod64.c b/llrtc/lib/sdivmod64.c new file mode 100644 index 0000000..df294b3 --- /dev/null +++ b/llrtc/lib/sdivmod64.c @@ -0,0 +1,40 @@ +#include "llrt.h" +#include + +/* +Calls to udivmod64 internally. +Note: remainder uses sign of divisor. +*/ +int64_t sdivmod64(int64_t dividend, int64_t divisor, int64_t *remainder) +{ + int signbitidx = BITS_PER_BYTE * sizeof(dividend) - 1; + int signed_dividend = dividend < 0; + int signed_divisor = divisor < 0; + int signed_result = signed_divisor ^ signed_dividend; + + int64_t quotient; + uint64_t udvd, udvr, uquotient, uremainder; + + udvd = signed_dividend ? -dividend : dividend; + udvr = signed_divisor ? -divisor : divisor; + uquotient = udivmod64(udvd, udvr, &uremainder); + + if (signed_result){ + if (uremainder) { + quotient = -(int64_t)uquotient - 1; + } else { + quotient = -(int64_t)uquotient; + } + if (remainder) { + /* if signed, there could be unsigned overflow + causing undefined behavior */ + *remainder = (uint64_t)dividend - (uint64_t)quotient * (uint64_t)divisor; + } + } else { + quotient = (int64_t)uquotient; + if (remainder) { + *remainder = signed_divisor ? -uremainder : uremainder; + } + } + return quotient; +} diff --git a/llrtc/lib/test_sdivmod64.c b/llrtc/lib/test_sdivmod64.c new file mode 100644 index 0000000..1e804d6 --- /dev/null +++ b/llrtc/lib/test_sdivmod64.c @@ -0,0 +1,21 @@ +#include +#include +#include "llrt.h" + +int main(int argc, char * argv[]){ + int64_t n, d, q, r; + + if (argc != 3) { + printf("invalid argument: %s dividend divisor", argv[0]); + return 1; + } + sscanf(argv[1], "%lld", &n); + sscanf(argv[2], "%lld", &d); + + q = sdivmod64(n, d, &r); + + printf("%lld\n", q); + printf("%lld\n", r); + + return 0; +} diff --git a/llrtc/lib/test_sdivmod64.py b/llrtc/lib/test_sdivmod64.py new file mode 100644 index 0000000..4aa49a9 --- /dev/null +++ b/llrtc/lib/test_sdivmod64.py @@ -0,0 +1,56 @@ +import math +import os +import subprocess +udt = os.path.join('.', 'test_sdivmod64.run') + +def testcase(dividend, divisor): + print 'divmod64(%d, %d)' % (dividend, divisor) + + procargs = ('%s %s %s' % (udt, dividend, divisor)).split() + result = subprocess.check_output(procargs) + gotQ, gotR = map(int, result.splitlines()) + + expectQ = dividend // divisor + expectR = dividend % divisor + + print 'Q = %d, R = %d' % (gotQ, gotR) + + if expectQ != gotQ: + raise ValueError("invalid quotient: got=%d but expect=%d" % + (gotQ, expectQ)) + if expectR != gotR: + raise ValueError("invalid remainder: got=%d but expect=%d" % + (gotR, expectR)) + print 'OK' + +def testsequence(): + subjects = [ + (0, 1), + (0, 0xffffffff), + (1, 2), + (1, 983219), + (2, 2), + (3, 2), + (1024, 2), + (2048, 512), + (21321, 512), + (9329189, 1031), + (0xffffffff, 2), + (0xffffffff, 0xffff), + (0x1ffffffff, 2), + (0x1ffffffff, 0xffff), + (0xffff, 0xffffffff), + (0x0fffffffffffffff, 0xffff), + (0x7fffffffffffffff, 0x7fffffffffffffff), + (0x7fffffffffffffff, 0x7ffffffffffffff0), + (0x7fffffffffffffff, 87655678587161901), + ] + + for dvd, dvr in subjects: + testcase(dvd, dvr) + testcase(dvd, -dvr) + testcase(-dvd, dvr) + testcase(-dvd, -dvr) + +if __name__ == '__main__': + testsequence() diff --git a/llrtc/lib/test_udivmod64.c b/llrtc/lib/test_udivmod64.c new file mode 100644 index 0000000..70b44aa --- /dev/null +++ b/llrtc/lib/test_udivmod64.c @@ -0,0 +1,20 @@ +#include +#include +#include "llrt.h" + +int main(int argc, char * argv[]){ + uint64_t n, d, q, r; + if (argc != 3) { + printf("invalid argument: %s dividend divisor", argv[0]); + return 1; + } + sscanf(argv[1], "%llu", &n); + sscanf(argv[2], "%llu", &d); + + q = udivmod64(n, d, &r); + + printf("%llu\n", q); + printf("%llu\n", r); + + return 0; +} diff --git a/llrtc/lib/test_udivmod64.py b/llrtc/lib/test_udivmod64.py new file mode 100644 index 0000000..da07ccb --- /dev/null +++ b/llrtc/lib/test_udivmod64.py @@ -0,0 +1,53 @@ +import math +import os +import subprocess +udt = os.path.join('.', 'test_udivmod64.run') + +def testcase(dividend, divisor): + print 'divmod64(%d, %d)' % (dividend, divisor) + + procargs = ('%s %s %s' % (udt, dividend, divisor)).split() + result = subprocess.check_output(procargs) + gotQ, gotR = map(int, result.splitlines()) + + expectQ = dividend // divisor + expectR = dividend % divisor + + print 'Q = %d, R = %d' % (gotQ, gotR) + + if expectQ != gotQ: + raise ValueError("invalid quotient: got=%d but expect=%d" % + (gotQ, expectQ)) + if expectR != gotR: + raise ValueError("invalid remainder: got=%d but expect=%d" % + (gotR, expectR)) + print 'OK' + +def testsequence(): + subjects = [ + (0, 1), + (0, 0xffffffffffffffff), + (1, 2), + (1, 983219), + (2, 2), + (3, 2), + (1024, 2), + (2048, 512), + (21321, 512), + (9329189, 1031), + (0xffffffff, 2), + (0xffffffff, 0xffff), + (0x1ffffffff, 2), + (0x1ffffffff, 0xffff), + (0xffff, 0xffffffff), + (0xffffffffffffffff, 0xffff), + (0xffffffffffffffff, 0x7fffffffffffffff), + (0xffffffffffffffff, 0xfffffffffffffff0), + (0xffffffffffffffff, 87655678587161901), + ] + + for dvd, dvr in subjects: + testcase(dvd, dvr) + +if __name__ == '__main__': + testsequence() diff --git a/llrtc/lib/udivmod64.c b/llrtc/lib/udivmod64.c new file mode 100644 index 0000000..5936f98 --- /dev/null +++ b/llrtc/lib/udivmod64.c @@ -0,0 +1,84 @@ +/* +Implements unsigned divmod using for platform missing 64-bit division and/or +modulo functions. +*/ +#include "llrt.h" + +/* +count left zero for 64-bit words + +*/ +static +int clz64(uint64_t x) +{ + const int total_bits = sizeof(x) * BITS_PER_BYTE; + int zc = 0; + + while (zc < total_bits && ((x >> (total_bits - zc - 1)) & 1) == 0) { + ++zc; + } + return zc; +} + +typedef struct div_state_ +{ + uint64_t tmp, dvd; +} div_state; + +/* +Left shift div_state by 1 bit +*/ +static +void div_state_lshift(div_state *state) +{ + state->tmp = (state->tmp << 1) | (state->dvd >> 63); + state->dvd = state->dvd << 1; +} + +/* +Division of unsigned 64-bit word using 64-bit addition and subtration following +the shift-restore division algorithm. +For those interested in 32-bit implementation, +mapping of 64-bit addition and subtraction to 32-bit should be trivial. + +Reference: + - IBM. The PowerPC Compiler Writer's Guide + - LLVM compiler-rt + +Assumptions: + - all operands and results are positive + - unsigned wrapped around +*/ +uint64_t udivmod64(uint64_t dividend, uint64_t divisor, uint64_t *remainder) +{ + div_state state = {0, dividend}; + uint64_t quotient = 0; + int i; + int skipahead; + + if (divisor == 0) { + return 1 / 0; /* intentionally div by zero */ + } + + /* + skipahead to reduce iteration + */ + skipahead = clz64(dividend); + + for (i = 0; i < skipahead; ++i) { + div_state_lshift(&state); + } + + /* + division loop + */ + for (i = skipahead; i < 64; ++i) { + div_state_lshift(&state); + if (state.tmp >= divisor) { + state.tmp = state.tmp - divisor; + quotient |= 1ull << (63 - i); + } + } + if (remainder) *remainder = state.tmp; + return quotient; +} diff --git a/llrtc/tools/striptriple.py b/llrtc/tools/striptriple.py new file mode 100644 index 0000000..a20ba34 --- /dev/null +++ b/llrtc/tools/striptriple.py @@ -0,0 +1,15 @@ +import sys +import re + +buf = [] +with open(sys.argv[1], 'r') as fin: + tripleline = re.compile('^target\s+triple\s+=\s+') + for line in fin.readlines(): + if not tripleline.match(line): + buf.append(line) + +with open(sys.argv[1], 'w') as fout: + for line in buf: + fout.write(line) + + diff --git a/llvm-config-win32.py b/llvm-config-win32.py index 05fc94f..3f756ee 100644 --- a/llvm-config-win32.py +++ b/llvm-config-win32.py @@ -2,6 +2,7 @@ import re import sys from distutils.spawn import find_executable from os.path import abspath, dirname, isfile, join +from os import listdir from subprocess import Popen, PIPE @@ -39,55 +40,17 @@ def libs_options(): # NOTE: instead of actually looking at the components requested, # we just print out a bunch of libs for lib in """ -LLVMAnalysis -LLVMAsmParser -LLVMAsmPrinter -LLVMBitReader -LLVMBitWriter -LLVMCodeGen -LLVMCore -LLVMExecutionEngine -LLVMInstCombine -LLVMInstrumentation -LLVMInterpreter -LLVMipa -LLVMipo -LLVMJIT -LLVMLinker -LLVMMC -LLVMMCParser -LLVMObject -LLVMRuntimeDyld -LLVMScalarOpts -LLVMSelectionDAG -LLVMSupport -LLVMTarget -LLVMTransformUtils -LLVMVectorize -LLVMX86AsmParser -LLVMX86AsmPrinter -LLVMX86CodeGen -LLVMX86Desc -LLVMX86Disassembler -LLVMX86Info -LLVMX86Utils Advapi32 Shell32 """.split(): print('-l%s' % lib) - if isfile(join(find_llvm_prefix(), 'lib', 'LLVMPTXCodeGen.lib')): - print('-lLLVMPTXAsmPrinter') - print('-lLLVMPTXCodeGen') - print('-lLLVMPTXDesc') - print('-lLLVMPTXInfo') - - elif isfile(join(find_llvm_prefix(), 'lib', 'LLVMNVPTXCodeGen.lib')): - print('-lLLVMNVPTXAsmPrinter') - print('-lLLVMNVPTXCodeGen') - print('-lLLVMNVPTXDesc') - print('-lLLVMNVPTXInfo') - + bpath = join(find_llvm_prefix(), 'lib') + for filename in listdir(bpath): + filepath = join(bpath, filename) + if isfile(filepath) and filename.endswith('.lib') and filename.startswith('LLVM'): + name = filename.split('.', 1)[0] + print('-l%s' % name) def main(): try: @@ -98,12 +61,15 @@ def main(): if option == '--version': print(get_llvm_version()) + elif option == '--targets-built': + print('X86') # just do X86 + elif option == '--libs': libs_options() elif option == '--includedir': incdir = join(find_llvm_prefix(), 'include') - ensure_file(join(incdir, 'llvm' , 'BasicBlock.h')) + ensure_file(join(incdir, 'llvm' , 'Linker.h')) print(incdir) elif option == '--libdir': diff --git a/llvm/__init__.py b/llvm/__init__.py index 8f71446..9b24112 100644 --- a/llvm/__init__.py +++ b/llvm/__init__.py @@ -4,10 +4,13 @@ del get_versions from llvmpy import extra + version = extra.get_llvm_version() del extra class Wrapper(object): + __slots__ = '__ptr' + def __init__(self, ptr): assert ptr self.__ptr = ptr @@ -31,14 +34,14 @@ def _extract_ptrs(objs): class LLVMException(Exception): pass -def test(verbosity=1): +def test(verbosity=3, run_isolated=True): """test(verbosity=1) -> TextTestResult Run self-test, and return the number of failures + errors """ - from llvm.test_llvmpy import run + from llvm.tests import run - result = run(verbosity=verbosity) - - return len(result.failures) + len(result.errors) + result = run(verbosity=verbosity, run_isolated=run_isolated) + errct = len(result.failures) + len(result.errors) + return errct diff --git a/llvm/core.py b/llvm/core.py index 478728b..4db8135 100644 --- a/llvm/core.py +++ b/llvm/core.py @@ -41,150 +41,203 @@ import contextlib, weakref import llvm from llvm._intrinsic_ids import * - +from llvm.deprecated import deprecated from llvmpy import api #===----------------------------------------------------------------------=== # Enumerations #===----------------------------------------------------------------------=== +class Enum(int): + '''Overload integer to print the name of the enum. + ''' + def __repr__(self): + return '%s(%d)' % (type(self).__name__, self) + + @classmethod + def declare(cls): + declared = cls._declared_ = {} + scope = globals() + for name in filter(lambda s: s.startswith(cls.prefix), dir(cls)): + n = getattr(cls, name) + typ = type(name, (cls,), {}) + obj = typ(n) + declared[n] = obj + scope[name] = obj + + @classmethod + def get(cls, num): + return cls._declared_[num] # type id (llvm::Type::TypeID) -TYPE_VOID = api.llvm.Type.TypeID.VoidTyID -TYPE_HALF = api.llvm.Type.TypeID.HalfTyID -TYPE_FLOAT = api.llvm.Type.TypeID.FloatTyID -TYPE_DOUBLE = api.llvm.Type.TypeID.DoubleTyID -TYPE_X86_FP80 = api.llvm.Type.TypeID.X86_FP80TyID -TYPE_FP128 = api.llvm.Type.TypeID.FP128TyID -TYPE_PPC_FP128 = api.llvm.Type.TypeID.PPC_FP128TyID -TYPE_LABEL = api.llvm.Type.TypeID.LabelTyID -TYPE_INTEGER = api.llvm.Type.TypeID.IntegerTyID -TYPE_FUNCTION = api.llvm.Type.TypeID.FunctionTyID -TYPE_STRUCT = api.llvm.Type.TypeID.StructTyID -TYPE_ARRAY = api.llvm.Type.TypeID.ArrayTyID -TYPE_POINTER = api.llvm.Type.TypeID.PointerTyID -TYPE_VECTOR = api.llvm.Type.TypeID.VectorTyID -TYPE_METADATA = api.llvm.Type.TypeID.MetadataTyID -TYPE_X86_MMX = api.llvm.Type.TypeID.X86_MMXTyID +class TypeEnum(Enum): + prefix = 'TYPE_' + TypeID = api.llvm.Type.TypeID + + TYPE_VOID = TypeID.VoidTyID + TYPE_HALF = TypeID.HalfTyID + TYPE_FLOAT = TypeID.FloatTyID + TYPE_DOUBLE = TypeID.DoubleTyID + TYPE_X86_FP80 = TypeID.X86_FP80TyID + TYPE_FP128 = TypeID.FP128TyID + TYPE_PPC_FP128 = TypeID.PPC_FP128TyID + TYPE_LABEL = TypeID.LabelTyID + TYPE_INTEGER = TypeID.IntegerTyID + TYPE_FUNCTION = TypeID.FunctionTyID + TYPE_STRUCT = TypeID.StructTyID + TYPE_ARRAY = TypeID.ArrayTyID + TYPE_POINTER = TypeID.PointerTyID + TYPE_VECTOR = TypeID.VectorTyID + TYPE_METADATA = TypeID.MetadataTyID + TYPE_X86_MMX = TypeID.X86_MMXTyID + +TypeEnum.declare() # value IDs (llvm::Value::ValueTy enum) # According to the doxygen docs, it is not a good idea to use these enums. # There are more values than those declared. -VALUE_ARGUMENT = api.llvm.Value.ValueTy.ArgumentVal -VALUE_BASIC_BLOCK = api.llvm.Value.ValueTy.BasicBlockVal -VALUE_FUNCTION = api.llvm.Value.ValueTy.FunctionVal -VALUE_GLOBAL_ALIAS = api.llvm.Value.ValueTy.GlobalAliasVal -VALUE_GLOBAL_VARIABLE = api.llvm.Value.ValueTy.GlobalVariableVal -VALUE_UNDEF_VALUE = api.llvm.Value.ValueTy.UndefValueVal -VALUE_BLOCK_ADDRESS = api.llvm.Value.ValueTy.BlockAddressVal -VALUE_CONSTANT_EXPR = api.llvm.Value.ValueTy.ConstantExprVal -VALUE_CONSTANT_AGGREGATE_ZERO = api.llvm.Value.ValueTy.ConstantAggregateZeroVal -VALUE_CONSTANT_DATA_ARRAY = api.llvm.Value.ValueTy.ConstantDataArrayVal -VALUE_CONSTANT_DATA_VECTOR = api.llvm.Value.ValueTy.ConstantDataVectorVal -VALUE_CONSTANT_INT = api.llvm.Value.ValueTy.ConstantIntVal -VALUE_CONSTANT_FP = api.llvm.Value.ValueTy.ConstantFPVal -VALUE_CONSTANT_ARRAY = api.llvm.Value.ValueTy.ConstantArrayVal -VALUE_CONSTANT_STRUCT = api.llvm.Value.ValueTy.ConstantStructVal -VALUE_CONSTANT_VECTOR = api.llvm.Value.ValueTy.ConstantVectorVal -VALUE_CONSTANT_POINTER_NULL = api.llvm.Value.ValueTy.ConstantPointerNullVal -VALUE_MD_NODE = api.llvm.Value.ValueTy.MDNodeVal -VALUE_MD_STRING = api.llvm.Value.ValueTy.MDStringVal -VALUE_INLINE_ASM = api.llvm.Value.ValueTy.InlineAsmVal -VALUE_PSEUDO_SOURCE_VALUE = api.llvm.Value.ValueTy.PseudoSourceValueVal -VALUE_FIXED_STACK_PSEUDO_SOURCE_VALUE = api.llvm.Value.ValueTy.FixedStackPseudoSourceValueVal -VALUE_INSTRUCTION = api.llvm.Value.ValueTy.InstructionVal +class ValueEnum(Enum): + prefix = 'VALUE_' + ValueTy = api.llvm.Value.ValueTy + + VALUE_ARGUMENT = ValueTy.ArgumentVal + VALUE_BASIC_BLOCK = ValueTy.BasicBlockVal + VALUE_FUNCTION = ValueTy.FunctionVal + VALUE_GLOBAL_ALIAS = ValueTy.GlobalAliasVal + VALUE_GLOBAL_VARIABLE = ValueTy.GlobalVariableVal + VALUE_UNDEF_VALUE = ValueTy.UndefValueVal + VALUE_BLOCK_ADDRESS = ValueTy.BlockAddressVal + VALUE_CONSTANT_EXPR = ValueTy.ConstantExprVal + VALUE_CONSTANT_AGGREGATE_ZERO = ValueTy.ConstantAggregateZeroVal + VALUE_CONSTANT_DATA_ARRAY = ValueTy.ConstantDataArrayVal + VALUE_CONSTANT_DATA_VECTOR = ValueTy.ConstantDataVectorVal + VALUE_CONSTANT_INT = ValueTy.ConstantIntVal + VALUE_CONSTANT_FP = ValueTy.ConstantFPVal + VALUE_CONSTANT_ARRAY = ValueTy.ConstantArrayVal + VALUE_CONSTANT_STRUCT = ValueTy.ConstantStructVal + VALUE_CONSTANT_VECTOR = ValueTy.ConstantVectorVal + VALUE_CONSTANT_POINTER_NULL = ValueTy.ConstantPointerNullVal + VALUE_MD_NODE = ValueTy.MDNodeVal + VALUE_MD_STRING = ValueTy.MDStringVal + VALUE_INLINE_ASM = ValueTy.InlineAsmVal + VALUE_PSEUDO_SOURCE_VALUE = ValueTy.PseudoSourceValueVal + VALUE_FIXED_STACK_PSEUDO_SOURCE_VALUE = ValueTy.FixedStackPseudoSourceValueVal + VALUE_INSTRUCTION = ValueTy.InstructionVal + +ValueEnum.declare() # instruction opcodes (from include/llvm/Instruction.def) -OPCODE_RET = 1 -OPCODE_BR = 2 -OPCODE_SWITCH = 3 -OPCODE_INDIRECT_BR = 4 -OPCODE_INVOKE = 5 -OPCODE_RESUME = 6 -OPCODE_UNREACHABLE = 7 -OPCODE_ADD = 8 -OPCODE_FADD = 9 -OPCODE_SUB = 10 -OPCODE_FSUB = 11 -OPCODE_MUL = 12 -OPCODE_FMUL = 13 -OPCODE_UDIV = 14 -OPCODE_SDIV = 15 -OPCODE_FDIV = 16 -OPCODE_UREM = 17 -OPCODE_SREM = 18 -OPCODE_FREM = 19 -OPCODE_SHL = 20 -OPCODE_LSHR = 21 -OPCODE_ASHR = 22 -OPCODE_AND = 23 -OPCODE_OR = 24 -OPCODE_XOR = 25 -OPCODE_ALLOCA = 26 -OPCODE_LOAD = 27 -OPCODE_STORE = 28 -OPCODE_GETELEMENTPTR = 29 -OPCODE_FENCE = 30 -OPCODE_ATOMICCMPXCHG = 31 -OPCODE_ATOMICRMW = 32 -OPCODE_TRUNC = 33 -OPCODE_ZEXT = 34 -OPCODE_SEXT = 35 -OPCODE_FPTOUI = 36 -OPCODE_FPTOSI = 37 -OPCODE_UITOFP = 38 -OPCODE_SITOFP = 39 -OPCODE_FPTRUNC = 40 -OPCODE_FPEXT = 41 -OPCODE_PTRTOINT = 42 -OPCODE_INTTOPTR = 43 -OPCODE_BITCAST = 44 -OPCODE_ICMP = 45 -OPCODE_FCMP = 46 -OPCODE_PHI = 47 -OPCODE_CALL = 48 -OPCODE_SELECT = 49 -OPCODE_USEROP1 = 50 -OPCODE_USEROP2 = 51 -OPCODE_VAARG = 52 -OPCODE_EXTRACTELEMENT = 53 -OPCODE_INSERTELEMENT = 54 -OPCODE_SHUFFLEVECTOR = 55 -OPCODE_EXTRACTVALUE = 56 -OPCODE_INSERTVALUE = 57 -OPCODE_LANDINGPAD = 58 +class OpcodeEnum(Enum): + prefix = 'OPCODE_' + + OPCODE_RET = 1 + OPCODE_BR = 2 + OPCODE_SWITCH = 3 + OPCODE_INDIRECT_BR = 4 + OPCODE_INVOKE = 5 + OPCODE_RESUME = 6 + OPCODE_UNREACHABLE = 7 + OPCODE_ADD = 8 + OPCODE_FADD = 9 + OPCODE_SUB = 10 + OPCODE_FSUB = 11 + OPCODE_MUL = 12 + OPCODE_FMUL = 13 + OPCODE_UDIV = 14 + OPCODE_SDIV = 15 + OPCODE_FDIV = 16 + OPCODE_UREM = 17 + OPCODE_SREM = 18 + OPCODE_FREM = 19 + OPCODE_SHL = 20 + OPCODE_LSHR = 21 + OPCODE_ASHR = 22 + OPCODE_AND = 23 + OPCODE_OR = 24 + OPCODE_XOR = 25 + OPCODE_ALLOCA = 26 + OPCODE_LOAD = 27 + OPCODE_STORE = 28 + OPCODE_GETELEMENTPTR = 29 + OPCODE_FENCE = 30 + OPCODE_ATOMICCMPXCHG = 31 + OPCODE_ATOMICRMW = 32 + OPCODE_TRUNC = 33 + OPCODE_ZEXT = 34 + OPCODE_SEXT = 35 + OPCODE_FPTOUI = 36 + OPCODE_FPTOSI = 37 + OPCODE_UITOFP = 38 + OPCODE_SITOFP = 39 + OPCODE_FPTRUNC = 40 + OPCODE_FPEXT = 41 + OPCODE_PTRTOINT = 42 + OPCODE_INTTOPTR = 43 + OPCODE_BITCAST = 44 + OPCODE_ICMP = 45 + OPCODE_FCMP = 46 + OPCODE_PHI = 47 + OPCODE_CALL = 48 + OPCODE_SELECT = 49 + OPCODE_USEROP1 = 50 + OPCODE_USEROP2 = 51 + OPCODE_VAARG = 52 + OPCODE_EXTRACTELEMENT = 53 + OPCODE_INSERTELEMENT = 54 + OPCODE_SHUFFLEVECTOR = 55 + OPCODE_EXTRACTVALUE = 56 + OPCODE_INSERTVALUE = 57 + OPCODE_LANDINGPAD = 58 + +OpcodeEnum.declare() # calling conventions -CC_C = api.llvm.CallingConv.ID.C -CC_FASTCALL = api.llvm.CallingConv.ID.Fast -CC_COLDCALL = api.llvm.CallingConv.ID.Cold -CC_GHC = api.llvm.CallingConv.ID.GHC -CC_X86_STDCALL = api.llvm.CallingConv.ID.X86_StdCall -CC_X86_FASTCALL = api.llvm.CallingConv.ID.X86_FastCall -CC_ARM_APCS = api.llvm.CallingConv.ID.ARM_APCS -CC_ARM_AAPCS = api.llvm.CallingConv.ID.ARM_AAPCS -CC_ARM_AAPCS_VFP = api.llvm.CallingConv.ID.ARM_AAPCS_VFP -CC_MSP430_INTR = api.llvm.CallingConv.ID.MSP430_INTR -CC_X86_THISCALL = api.llvm.CallingConv.ID.X86_ThisCall -CC_PTX_KERNEL = api.llvm.CallingConv.ID.PTX_Kernel -CC_PTX_DEVICE = api.llvm.CallingConv.ID.PTX_Device -CC_MBLAZE_INTR = api.llvm.CallingConv.ID.MBLAZE_INTR -CC_MBLAZE_SVOL = api.llvm.CallingConv.ID.MBLAZE_SVOL +class CCEnum(Enum): + prefix = 'CC_' + + ID = api.llvm.CallingConv.ID + + CC_C = ID.C + CC_FASTCALL = ID.Fast + CC_COLDCALL = ID.Cold + CC_GHC = ID.GHC + CC_X86_STDCALL = ID.X86_StdCall + CC_X86_FASTCALL = ID.X86_FastCall + CC_ARM_APCS = ID.ARM_APCS + CC_ARM_AAPCS = ID.ARM_AAPCS + CC_ARM_AAPCS_VFP = ID.ARM_AAPCS_VFP + CC_MSP430_INTR = ID.MSP430_INTR + CC_X86_THISCALL = ID.X86_ThisCall + CC_PTX_KERNEL = ID.PTX_Kernel + CC_PTX_DEVICE = ID.PTX_Device + + if llvm.version <= (3, 3): + CC_MBLAZE_INTR = ID.MBLAZE_INTR + CC_MBLAZE_SVOL = ID.MBLAZE_SVOL + +CCEnum.declare() # int predicates -ICMP_EQ = api.llvm.CmpInst.Predicate.ICMP_EQ -ICMP_NE = api.llvm.CmpInst.Predicate.ICMP_NE -ICMP_UGT = api.llvm.CmpInst.Predicate.ICMP_UGT -ICMP_UGE = api.llvm.CmpInst.Predicate.ICMP_UGE -ICMP_ULT = api.llvm.CmpInst.Predicate.ICMP_ULT -ICMP_ULE = api.llvm.CmpInst.Predicate.ICMP_ULE -ICMP_SGT = api.llvm.CmpInst.Predicate.ICMP_SGT -ICMP_SGE = api.llvm.CmpInst.Predicate.ICMP_SGE -ICMP_SLT = api.llvm.CmpInst.Predicate.ICMP_SLT -ICMP_SLE = api.llvm.CmpInst.Predicate.ICMP_SLE +class ICMPEnum(Enum): + prefix = 'ICMP_' + Predicate = api.llvm.CmpInst.Predicate + + ICMP_EQ = Predicate.ICMP_EQ + ICMP_NE = Predicate.ICMP_NE + ICMP_UGT = Predicate.ICMP_UGT + ICMP_UGE = Predicate.ICMP_UGE + ICMP_ULT = Predicate.ICMP_ULT + ICMP_ULE = Predicate.ICMP_ULE + ICMP_SGT = Predicate.ICMP_SGT + ICMP_SGE = Predicate.ICMP_SGE + ICMP_SLT = Predicate.ICMP_SLT + ICMP_SLE = Predicate.ICMP_SLE + +ICMPEnum.declare() # same as ICMP_xx, for backward compatibility + IPRED_EQ = ICMP_EQ IPRED_NE = ICMP_NE IPRED_UGT = ICMP_UGT @@ -197,24 +250,33 @@ IPRED_SLT = ICMP_SLT IPRED_SLE = ICMP_SLE # real predicates -FCMP_FALSE = api.llvm.CmpInst.Predicate.FCMP_FALSE -FCMP_OEQ = api.llvm.CmpInst.Predicate.FCMP_OEQ -FCMP_OGT = api.llvm.CmpInst.Predicate.FCMP_OGT -FCMP_OGE = api.llvm.CmpInst.Predicate.FCMP_OGE -FCMP_OLT = api.llvm.CmpInst.Predicate.FCMP_OLT -FCMP_OLE = api.llvm.CmpInst.Predicate.FCMP_OLE -FCMP_ONE = api.llvm.CmpInst.Predicate.FCMP_ONE -FCMP_ORD = api.llvm.CmpInst.Predicate.FCMP_ORD -FCMP_UNO = api.llvm.CmpInst.Predicate.FCMP_UNO -FCMP_UEQ = api.llvm.CmpInst.Predicate.FCMP_UEQ -FCMP_UGT = api.llvm.CmpInst.Predicate.FCMP_UGT -FCMP_UGE = api.llvm.CmpInst.Predicate.FCMP_UGE -FCMP_ULT = api.llvm.CmpInst.Predicate.FCMP_ULT -FCMP_ULE = api.llvm.CmpInst.Predicate.FCMP_ULE -FCMP_UNE = api.llvm.CmpInst.Predicate.FCMP_UNE -FCMP_TRUE = api.llvm.CmpInst.Predicate.FCMP_TRUE + +class FCMPEnum(Enum): + prefix = 'FCMP_' + + Predicate = api.llvm.CmpInst.Predicate + + FCMP_FALSE = Predicate.FCMP_FALSE + FCMP_OEQ = Predicate.FCMP_OEQ + FCMP_OGT = Predicate.FCMP_OGT + FCMP_OGE = Predicate.FCMP_OGE + FCMP_OLT = Predicate.FCMP_OLT + FCMP_OLE = Predicate.FCMP_OLE + FCMP_ONE = Predicate.FCMP_ONE + FCMP_ORD = Predicate.FCMP_ORD + FCMP_UNO = Predicate.FCMP_UNO + FCMP_UEQ = Predicate.FCMP_UEQ + FCMP_UGT = Predicate.FCMP_UGT + FCMP_UGE = Predicate.FCMP_UGE + FCMP_ULT = Predicate.FCMP_ULT + FCMP_ULE = Predicate.FCMP_ULE + FCMP_UNE = Predicate.FCMP_UNE + FCMP_TRUE = Predicate.FCMP_TRUE + +FCMPEnum.declare() # real predicates + RPRED_FALSE = FCMP_FALSE RPRED_OEQ = FCMP_OEQ RPRED_OGT = FCMP_OGT @@ -233,68 +295,75 @@ RPRED_UNE = FCMP_UNE RPRED_TRUE = FCMP_TRUE # linkages (see llvm::GlobalValue::LinkageTypes) -LINKAGE_EXTERNAL = \ - api.llvm.GlobalValue.LinkageTypes.ExternalLinkage -LINKAGE_AVAILABLE_EXTERNALLY = \ - api.llvm.GlobalValue.LinkageTypes.AvailableExternallyLinkage -LINKAGE_LINKONCE_ANY = \ - api.llvm.GlobalValue.LinkageTypes.LinkOnceAnyLinkage -LINKAGE_LINKONCE_ODR = \ - api.llvm.GlobalValue.LinkageTypes.LinkOnceODRLinkage -LINKAGE_WEAK_ANY = \ - api.llvm.GlobalValue.LinkageTypes.WeakAnyLinkage -LINKAGE_WEAK_ODR = \ - api.llvm.GlobalValue.LinkageTypes.WeakODRLinkage -LINKAGE_APPENDING = \ - api.llvm.GlobalValue.LinkageTypes.AppendingLinkage -LINKAGE_INTERNAL = \ - api.llvm.GlobalValue.LinkageTypes.InternalLinkage -LINKAGE_PRIVATE = \ - api.llvm.GlobalValue.LinkageTypes.PrivateLinkage -LINKAGE_DLLIMPORT = \ - api.llvm.GlobalValue.LinkageTypes.DLLImportLinkage -LINKAGE_DLLEXPORT = \ - api.llvm.GlobalValue.LinkageTypes.DLLExportLinkage -LINKAGE_EXTERNAL_WEAK = \ - api.llvm.GlobalValue.LinkageTypes.ExternalWeakLinkage -LINKAGE_COMMON = \ - api.llvm.GlobalValue.LinkageTypes.CommonLinkage -LINKAGE_LINKER_PRIVATE = \ - api.llvm.GlobalValue.LinkageTypes.LinkerPrivateLinkage -LINKAGE_LINKER_PRIVATE_WEAK = \ - api.llvm.GlobalValue.LinkageTypes.LinkerPrivateWeakLinkage +class LinkageEnum(Enum): + prefix = 'LINKAGE_' + LinkageTypes = api.llvm.GlobalValue.LinkageTypes + + LINKAGE_EXTERNAL = LinkageTypes.ExternalLinkage + LINKAGE_AVAILABLE_EXTERNALLY = LinkageTypes.AvailableExternallyLinkage + LINKAGE_LINKONCE_ANY = LinkageTypes.LinkOnceAnyLinkage + LINKAGE_LINKONCE_ODR = LinkageTypes.LinkOnceODRLinkage + LINKAGE_WEAK_ANY = LinkageTypes.WeakAnyLinkage + LINKAGE_WEAK_ODR = LinkageTypes.WeakODRLinkage + LINKAGE_APPENDING = LinkageTypes.AppendingLinkage + LINKAGE_INTERNAL = LinkageTypes.InternalLinkage + LINKAGE_PRIVATE = LinkageTypes.PrivateLinkage + LINKAGE_DLLIMPORT = LinkageTypes.DLLImportLinkage + LINKAGE_DLLEXPORT = LinkageTypes.DLLExportLinkage + LINKAGE_EXTERNAL_WEAK = LinkageTypes.ExternalWeakLinkage + LINKAGE_COMMON = LinkageTypes.CommonLinkage + LINKAGE_LINKER_PRIVATE = LinkageTypes.LinkerPrivateLinkage + LINKAGE_LINKER_PRIVATE_WEAK = LinkageTypes.LinkerPrivateWeakLinkage + +LinkageEnum.declare() # visibility (see llvm/GlobalValue.h) -VISIBILITY_DEFAULT = api.llvm.GlobalValue.VisibilityTypes.DefaultVisibility -VISIBILITY_HIDDEN = api.llvm.GlobalValue.VisibilityTypes.HiddenVisibility -VISIBILITY_PROTECTED = api.llvm.GlobalValue.VisibilityTypes.ProtectedVisibility +class VisibilityEnum(Enum): + prefix = 'VISIBILITY_' -# parameter attributes llvm::Attributes::AttrVal (see llvm/Attributes.h) -ATTR_NONE = api.llvm.Attributes.AttrVal.None_ -ATTR_ZEXT = api.llvm.Attributes.AttrVal.ZExt -ATTR_SEXT = api.llvm.Attributes.AttrVal.SExt -ATTR_NO_RETURN = api.llvm.Attributes.AttrVal.NoReturn -ATTR_IN_REG = api.llvm.Attributes.AttrVal.InReg -ATTR_STRUCT_RET = api.llvm.Attributes.AttrVal.StructRet -ATTR_NO_UNWIND = api.llvm.Attributes.AttrVal.NoUnwind -ATTR_NO_ALIAS = api.llvm.Attributes.AttrVal.NoAlias -ATTR_BY_VAL = api.llvm.Attributes.AttrVal.ByVal -ATTR_NEST = api.llvm.Attributes.AttrVal.Nest -ATTR_READ_NONE = api.llvm.Attributes.AttrVal.ReadNone -ATTR_READONLY = api.llvm.Attributes.AttrVal.ReadOnly -ATTR_NO_INLINE = api.llvm.Attributes.AttrVal.NoInline -ATTR_ALWAYS_INLINE = api.llvm.Attributes.AttrVal.AlwaysInline -ATTR_OPTIMIZE_FOR_SIZE = api.llvm.Attributes.AttrVal.OptimizeForSize -ATTR_STACK_PROTECT = api.llvm.Attributes.AttrVal.StackProtect -ATTR_STACK_PROTECT_REQ = api.llvm.Attributes.AttrVal.StackProtectReq -ATTR_ALIGNMENT = api.llvm.Attributes.AttrVal.Alignment -ATTR_NO_CAPTURE = api.llvm.Attributes.AttrVal.NoCapture -ATTR_NO_REDZONE = api.llvm.Attributes.AttrVal.NoRedZone -ATTR_NO_IMPLICIT_FLOAT = api.llvm.Attributes.AttrVal.NoImplicitFloat -ATTR_NAKED = api.llvm.Attributes.AttrVal.Naked -ATTR_INLINE_HINT = api.llvm.Attributes.AttrVal.InlineHint -ATTR_STACK_ALIGNMENT = api.llvm.Attributes.AttrVal.StackAlignment + VISIBILITY_DEFAULT = api.llvm.GlobalValue.VisibilityTypes.DefaultVisibility + VISIBILITY_HIDDEN = api.llvm.GlobalValue.VisibilityTypes.HiddenVisibility + VISIBILITY_PROTECTED = api.llvm.GlobalValue.VisibilityTypes.ProtectedVisibility +VisibilityEnum.declare() + +# parameter attributes +# LLVM 3.2 llvm::Attributes::AttrVal (see llvm/Attributes.h) +# LLVM 3.3 llvm::Attribute::AttrKind (see llvm/Attributes.h) +class AttrEnum(Enum): + prefix = 'ATTR_' + + if llvm.version >= (3, 3): + AttrVal = api.llvm.Attribute.AttrKind + else: + AttrVal = api.llvm.Attributes.AttrVal + + ATTR_NONE = AttrVal.None_ + ATTR_ZEXT = AttrVal.ZExt + ATTR_SEXT = AttrVal.SExt + ATTR_NO_RETURN = AttrVal.NoReturn + ATTR_IN_REG = AttrVal.InReg + ATTR_STRUCT_RET = AttrVal.StructRet + ATTR_NO_UNWIND = AttrVal.NoUnwind + ATTR_NO_ALIAS = AttrVal.NoAlias + ATTR_BY_VAL = AttrVal.ByVal + ATTR_NEST = AttrVal.Nest + ATTR_READ_NONE = AttrVal.ReadNone + ATTR_READONLY = AttrVal.ReadOnly + ATTR_NO_INLINE = AttrVal.NoInline + ATTR_ALWAYS_INLINE = AttrVal.AlwaysInline + ATTR_OPTIMIZE_FOR_SIZE = AttrVal.OptimizeForSize + ATTR_STACK_PROTECT = AttrVal.StackProtect + ATTR_STACK_PROTECT_REQ = AttrVal.StackProtectReq + ATTR_ALIGNMENT = AttrVal.Alignment + ATTR_NO_CAPTURE = AttrVal.NoCapture + ATTR_NO_REDZONE = AttrVal.NoRedZone + ATTR_NO_IMPLICIT_FLOAT = AttrVal.NoImplicitFloat + ATTR_NAKED = AttrVal.Naked + ATTR_INLINE_HINT = AttrVal.InlineHint + ATTR_STACK_ALIGNMENT = AttrVal.StackAlignment + +AttrEnum.declare() class Module(llvm.Wrapper): """A Module instance stores all the information related to an LLVM module. @@ -310,6 +379,7 @@ class Module(llvm.Wrapper): module_obj = Module.new('my_module') """ + __slots__ = '__weakref__' __cache = weakref.WeakValueDictionary() def __new__(cls, ptr): @@ -382,12 +452,12 @@ class Module(llvm.Wrapper): """ return str(self._ptr) + def __hash__(self): + return id(self._ptr) + def __eq__(self, rhs): - assert isinstance(rhs, Module), type(rhs) if isinstance(rhs, Module): - return str(self) == str(rhs) - else: - return False + return self._ptr == rhs._ptr def __ne__(self, rhs): return not (self == rhs) @@ -443,7 +513,7 @@ class Module(llvm.Wrapper): mode, errmsg) if failed: - raise llvm.LLVMException(errmsg) + raise llvm.LLVMException(errmsg.getvalue()) def get_type_named(self, name): typ = self._ptr.getTypeByName(name) @@ -535,7 +605,7 @@ class Module(llvm.Wrapper): ret = False if fileobj is None: ret = True - fileobj = BytesIO + fileobj = BytesIO() api.llvm.WriteBitcodeToFile(self._ptr, fileobj) if ret: return fileobj.getvalue() @@ -619,6 +689,7 @@ class Type(llvm.Wrapper): Use one of the static methods to create an instance. Example: ty = Type.double() """ + __slots__ = '__name__' _type_ = api.llvm.Type def __init__(self, ptr): @@ -800,6 +871,9 @@ class Type(llvm.Wrapper): def __str__(self): return str(self._ptr) + def __hash__(self): + return hash(self._ptr) + def __eq__(self, rhs): return self._ptr is rhs._ptr @@ -808,6 +882,7 @@ class Type(llvm.Wrapper): class IntegerType(Type): """Represents an integer type.""" + __slots__ = () _type_ = api.llvm.IntegerType @property @@ -817,6 +892,7 @@ class IntegerType(Type): class FunctionType(Type): """Represents a function type.""" + __slots__ = () _type_ = api.llvm.FunctionType @property @@ -846,6 +922,7 @@ class FunctionType(Type): class StructType(Type): """Represents a structure type.""" _type_ = api.llvm.StructType + __slots__ = () @property def element_count(self): @@ -879,7 +956,10 @@ class StructType(Type): self._ptr.setName(name) def _get_name(self): - return self._ptr.getName() + if self._ptr.isLiteral(): + return "" + else: + return self._ptr.getName() name = property(_get_name, _set_name) @@ -898,10 +978,10 @@ class StructType(Type): def is_layout_identical(self, other): return self._ptr.isLayoutIdentical(other._ptr) - class ArrayType(Type): """Represents an array type.""" _type_ = api.llvm.ArrayType + __slots__ = () @property def element(self): @@ -913,6 +993,7 @@ class ArrayType(Type): class PointerType(Type): _type_ = api.llvm.PointerType + __slots__ = () @property def pointee(self): @@ -924,6 +1005,7 @@ class PointerType(Type): class VectorType(Type): _type_ = api.llvm.VectorType + __slots__ = () @property def element(self): @@ -935,6 +1017,7 @@ class VectorType(Type): class Value(llvm.Wrapper): _type_ = api.llvm.Value + __slots__ = '__weakref__' def __init__(self, builder, ptr): assert builder is _ValueFactory @@ -965,6 +1048,9 @@ class Value(llvm.Wrapper): def __str__(self): return str(self._ptr) + def __hash__(self): + return hash(self._ptr) + def __eq__(self, rhs): if isinstance(rhs, Value): return str(self) == str(rhs) @@ -1000,6 +1086,7 @@ class Value(llvm.Wrapper): class User(Value): _type_ = api.llvm.User + __slots__ = () @property def operand_count(self): @@ -1014,6 +1101,7 @@ class User(Value): class Constant(User): _type_ = api.llvm.Constant + __slots__ = () @staticmethod def null(ty): @@ -1199,6 +1287,7 @@ class Constant(User): class ConstantExpr(Constant): _type_ = api.llvm.ConstantExpr + __slots__ = () @property def opcode(self): @@ -1209,19 +1298,20 @@ class ConstantExpr(Constant): return self._ptr.getOpcodeName() class ConstantAggregateZero(Constant): - pass + __slots__ = () class ConstantDataArray(Constant): - pass + __slots__ = () class ConstantDataVector(Constant): - pass + __slots__ = () class ConstantInt(Constant): _type_ = api.llvm.ConstantInt + __slots__ = () @property def z_ext_value(self): @@ -1237,30 +1327,32 @@ class ConstantInt(Constant): class ConstantFP(Constant): - pass + __slots__ = () class ConstantArray(Constant): - pass + __slots__ = () class ConstantStruct(Constant): - pass + __slots__ = () class ConstantVector(Constant): - pass + __slots__ = () class ConstantPointerNull(Constant): - pass + __slots__ = () class UndefValue(Constant): - pass + __slots__ = () + class GlobalValue(Constant): _type_ = api.llvm.GlobalValue + __slots__ = () def _get_linkage(self): return self._ptr.getLinkage() @@ -1306,6 +1398,7 @@ class GlobalValue(Constant): class GlobalVariable(GlobalValue): _type_ = api.llvm.GlobalVariable + __slots__ = () @staticmethod def new(module, ty, name, addrspace=0): @@ -1366,28 +1459,60 @@ class GlobalVariable(GlobalValue): thread_local = property(_get_thread_local, _set_thread_local) class Argument(Value): + __slots__ = () _type_ = api.llvm.Argument + _valid_attrs = frozenset([ATTR_BY_VAL, ATTR_NEST, ATTR_NO_ALIAS, + ATTR_NO_CAPTURE, ATTR_STRUCT_RET]) - def add_attribute(self, attr): - context = api.llvm.getGlobalContext() - attrbldr = api.llvm.AttrBuilder.new() - attrbldr.addAttribute(attr) - attrs = api.llvm.Attributes.get(context, attrbldr) - self._ptr.addAttr(attrs) + if llvm.version >= (3, 3): + def add_attribute(self, attr): + context = api.llvm.getGlobalContext() + attrbldr = api.llvm.AttrBuilder.new() + attrbldr.addAttribute(attr) + attrs = api.llvm.AttributeSet.get(context, 0, attrbldr) + self._ptr.addAttr(attrs) - def remove_attribute(self, attr): - context = api.llvm.getGlobalContext() - attrbldr = api.llvm.AttrBuilder.new() - attrbldr.addAttribute(attr) - attrs = api.llvm.Attributes.get(context, attrbldr) - self._ptr.removeAttr(attrs) + if attr not in self: + raise ValueError("Attribute %r is not valid for arg %s" % + (attr, self)) - def _set_alignment(self, align): - context = api.llvm.getGlobalContext() - attrbldr = api.llvm.AttrBuilder.new() - attrbldr.addAlignmentAttr(align) - attrs = api.llvm.Attributes.get(context, attrbldr) - self._ptr.addAttr(attrs) + def remove_attribute(self, attr): + context = api.llvm.getGlobalContext() + attrbldr = api.llvm.AttrBuilder.new() + attrbldr.addAttribute(attr) + attrs = api.llvm.AttributeSet.get(context, 0, attrbldr) + self._ptr.removeAttr(attrs) + + def _set_alignment(self, align): + context = api.llvm.getGlobalContext() + attrbldr = api.llvm.AttrBuilder.new() + attrbldr.addAlignmentAttr(align) + attrs = api.llvm.AttributeSet.get(context, 0, attrbldr) + self._ptr.addAttr(attrs) + else: + def add_attribute(self, attr): + context = api.llvm.getGlobalContext() + attrbldr = api.llvm.AttrBuilder.new() + attrbldr.addAttribute(attr) + attrs = api.llvm.Attributes.get(context, attrbldr) + self._ptr.addAttr(attrs) + if attr not in self: + raise ValueError("Attribute %r is not valid for arg %s" % + (attr, self)) + + def remove_attribute(self, attr): + context = api.llvm.getGlobalContext() + attrbldr = api.llvm.AttrBuilder.new() + attrbldr.addAttribute(attr) + attrs = api.llvm.Attributes.get(context, attrbldr) + self._ptr.removeAttr(attrs) + + def _set_alignment(self, align): + context = api.llvm.getGlobalContext() + attrbldr = api.llvm.AttrBuilder.new() + attrbldr.addAlignmentAttr(align) + attrs = api.llvm.Attributes.get(context, attrbldr) + self._ptr.addAttr(attrs) def _get_alignment(self): return self._ptr.getParamAlignment() @@ -1395,7 +1520,47 @@ class Argument(Value): alignment = property(_get_alignment, _set_alignment) + @property + def attributes(self): + '''Returns a set of defined attributes. + ''' + return set(attr for attr in self._valid_attrs if attr in self) + + def __contains__(self, attr): + if attr == ATTR_BY_VAL: + return self.has_by_val() + elif attr == ATTR_NEST: + return self.has_nest() + elif attr == ATTR_NO_ALIAS: + return self.has_no_alias() + elif attr == ATTR_NO_CAPTURE: + return self.has_no_capture() + elif attr == ATTR_STRUCT_RET: + return self.has_struct_ret() + else: + raise ValueError('invalid attribute for argument') + + @property + def arg_no(self): + return self._ptr.getArgNo() + + def has_by_val(self): + return self._ptr.hasByValAttr() + + def has_nest(self): + return self._ptr.hasNestAttr() + + def has_no_alias(self): + return self._ptr.hasNoAliasAttr() + + def has_no_capture(self): + return self._ptr.hasNoCaptureAttr() + + def has_struct_ret(self): + return self._ptr.hasStructRetAttr() + class Function(GlobalValue): + __slots__ = () _type_ = api.llvm.Function @staticmethod @@ -1505,7 +1670,10 @@ class Function(GlobalValue): context = api.llvm.getGlobalContext() attrbldr = api.llvm.AttrBuilder.new() attrbldr.addAttribute(attr) - attrs = api.llvm.Attributes.get(context, attrbldr) + if llvm.version >= (3, 3): + attrs = api.llvm.Attribute.get(context, attrbldr) + else: + attrs = api.llvm.Attributes.get(context, attrbldr) self._ptr.removeFnAttr(attrs) def viewCFGOnly(self): @@ -1531,6 +1699,7 @@ class Function(GlobalValue): #===----------------------------------------------------------------------=== class InlineAsm(Value): + __slots__ = () _type_ = api.llvm.InlineAsm @staticmethod @@ -1545,6 +1714,7 @@ class InlineAsm(Value): #===----------------------------------------------------------------------=== class MetaData(Value): + __slots__ = () _type_ = api.llvm.MDNode @staticmethod @@ -1601,6 +1771,7 @@ class MetaDataString(Value): class NamedMetaData(llvm.Wrapper): + __slots__ = () @staticmethod def get_or_insert(mod, name): @@ -1630,6 +1801,7 @@ class NamedMetaData(llvm.Wrapper): #===----------------------------------------------------------------------=== class Instruction(User): + __slots__ = () _type_ = api.llvm.Instruction @property @@ -1706,8 +1878,12 @@ class Instruction(User): def erase_from_parent(self): return self._ptr.eraseFromParent() + def replace_all_uses_with(self, inst): + self._ptr.replaceAllUsesWith(inst) + class CallOrInvokeInstruction(Instruction): + __slots__ = () _type_ = api.llvm.CallInst, api.llvm.InvokeInst def _get_cc(self): @@ -1722,21 +1898,33 @@ class CallOrInvokeInstruction(Instruction): context = api.llvm.getGlobalContext() attrbldr = api.llvm.AttrBuilder.new() attrbldr.addAttribute(attr) - attrs = api.llvm.Attributes.get(context, attrbldr) + if llvm.version >= (3, 3): + attrs = api.llvm.Attribute.get(context, attrbldr) + else: + attrs = api.llvm.Attributes.get(context, attrbldr) + self._ptr.addAttribute(idx, attrs) def remove_parameter_attribute(self, idx, attr): context = api.llvm.getGlobalContext() attrbldr = api.llvm.AttrBuilder.new() attrbldr.addAttribute(attr) - attrs = api.llvm.Attributes.get(context, attrbldr) + if llvm.version >= (3, 3): + attrs = api.llvm.Attribute.get(context, attrbldr) + else: + attrs = api.llvm.Attributes.get(context, attrbldr) + self._ptr.removeAttribute(idx, attrs) def set_parameter_alignment(self, idx, align): context = api.llvm.getGlobalContext() attrbldr = api.llvm.AttrBuilder.new() attrbldr.addAlignmentAttr(align) - attrs = api.llvm.Attributes.get(context, attrbldr) + if llvm.version >= (3, 3): + attrs = api.llvm.Attribute.get(context, attrbldr) + else: + attrs = api.llvm.Attributes.get(context, attrbldr) + self._ptr.addAttribute(idx, attrs) def _get_called_function(self): @@ -1751,6 +1939,7 @@ class CallOrInvokeInstruction(Instruction): class PHINode(Instruction): + __slots__ = () _type_ = api.llvm.PHINode @property @@ -1768,21 +1957,60 @@ class PHINode(Instruction): class SwitchInstruction(Instruction): + __slots__ = () + _type_ = api.llvm.SwitchInst def add_case(self, const, bblk): self._ptr.addCase(const._ptr, bblk._ptr) class CompareInstruction(Instruction): + __slots__ = () + _type_ = api.llvm.CmpInst @property def predicate(self): - return self._ptr.getPredicate() + n = self._ptr.getPredicate() + try: + return ICMPEnum.get(n) + except KeyError: + return FCMPEnum.get(n) + + +class AllocaInstruction(Instruction): + __slots__ = () + _type_ = api.llvm.AllocaInst + + @property + def alignment(self): + return self._ptr.getAlignment() + + @alignment.setter + def alignment(self, n): + self._ptr.setAlignment(n) + + @property + def array_size(self): + return self._ptr.getArraySize() + + @array_size.setter + def array_size(self, value): + return self._ptr.setArraySize(value._ptr)._ptr + + @property + def is_array(self): + return self._ptr.isArrayAllocation() + + @property + def is_static(self): + return self._ptr.isStaticAlloca() + #===----------------------------------------------------------------------=== # Basic block #===----------------------------------------------------------------------=== class BasicBlock(Value): + __slots__ = () _type_ = api.llvm.BasicBlock def insert_before(self, name): @@ -1809,6 +2037,7 @@ class BasicBlock(Value): class _ValueFactory(object): + __slots__ = () cache = weakref.WeakValueDictionary() # value ID -> class map @@ -1837,7 +2066,8 @@ class _ValueFactory(object): VALUE_INSTRUCTION + OPCODE_INVOKE : CallOrInvokeInstruction, VALUE_INSTRUCTION + OPCODE_SWITCH : SwitchInstruction, VALUE_INSTRUCTION + OPCODE_ICMP : CompareInstruction, - VALUE_INSTRUCTION + OPCODE_FCMP : CompareInstruction + VALUE_INSTRUCTION + OPCODE_FCMP : CompareInstruction, + VALUE_INSTRUCTION + OPCODE_ALLOCA : AllocaInstruction, } @classmethod @@ -1884,6 +2114,7 @@ _atomic_orderings = { } class Builder(llvm.Wrapper): + __slots__ = () @staticmethod def new(basic_block): @@ -1974,29 +2205,34 @@ class Builder(llvm.Wrapper): # arithmethic, bitwise and logical - def add(self, lhs, rhs, name=""): - return _make_value(self._ptr.CreateAdd(lhs._ptr, rhs._ptr, name)) + def add(self, lhs, rhs, name="", nuw=False, nsw=False): + return _make_value(self._ptr.CreateAdd(lhs._ptr, rhs._ptr, name, + nuw, nsw)) def fadd(self, lhs, rhs, name=""): return _make_value(self._ptr.CreateFAdd(lhs._ptr, rhs._ptr, name)) - def sub(self, lhs, rhs, name=""): - return _make_value(self._ptr.CreateSub(lhs._ptr, rhs._ptr, name)) + def sub(self, lhs, rhs, name="", nuw=False, nsw=False): + return _make_value(self._ptr.CreateSub(lhs._ptr, rhs._ptr, name, + nuw, nsw)) def fsub(self, lhs, rhs, name=""): return _make_value(self._ptr.CreateFSub(lhs._ptr, rhs._ptr, name)) - def mul(self, lhs, rhs, name=""): - return _make_value(self._ptr.CreateMul(lhs._ptr, rhs._ptr, name)) + def mul(self, lhs, rhs, name="", nuw=False, nsw=False): + return _make_value(self._ptr.CreateMul(lhs._ptr, rhs._ptr, name, + nuw, nsw)) def fmul(self, lhs, rhs, name=""): return _make_value(self._ptr.CreateFMul(lhs._ptr, rhs._ptr, name)) - def udiv(self, lhs, rhs, name=""): - return _make_value(self._ptr.CreateUDiv(lhs._ptr, rhs._ptr, name)) + def udiv(self, lhs, rhs, name="", exact=False): + return _make_value(self._ptr.CreateUDiv(lhs._ptr, rhs._ptr, name, + exact)) - def sdiv(self, lhs, rhs, name=""): - return _make_value(self._ptr.CreateSDiv(lhs._ptr, rhs._ptr, name)) + def sdiv(self, lhs, rhs, name="", exact=False): + return _make_value(self._ptr.CreateSDiv(lhs._ptr, rhs._ptr, name, + exact)) def fdiv(self, lhs, rhs, name=""): return _make_value(self._ptr.CreateFDiv(lhs._ptr, rhs._ptr, name)) @@ -2010,14 +2246,17 @@ class Builder(llvm.Wrapper): def frem(self, lhs, rhs, name=""): return _make_value(self._ptr.CreateFRem(lhs._ptr, rhs._ptr, name)) - def shl(self, lhs, rhs, name=""): - return _make_value(self._ptr.CreateShl(lhs._ptr, rhs._ptr, name)) + def shl(self, lhs, rhs, name="", nuw=False, nsw=False): + return _make_value(self._ptr.CreateShl(lhs._ptr, rhs._ptr, name, + nuw, nsw)) - def lshr(self, lhs, rhs, name=""): - return _make_value(self._ptr.CreateLShr(lhs._ptr, rhs._ptr, name)) + def lshr(self, lhs, rhs, name="", exact=False): + return _make_value(self._ptr.CreateLShr(lhs._ptr, rhs._ptr, name, + exact)) - def ashr(self, lhs, rhs, name=""): - return _make_value(self._ptr.CreateAShr(lhs._ptr, rhs._ptr, name)) + def ashr(self, lhs, rhs, name="", exact=False): + return _make_value(self._ptr.CreateAShr(lhs._ptr, rhs._ptr, name, + exact)) def and_(self, lhs, rhs, name=""): return _make_value(self._ptr.CreateAnd(lhs._ptr, rhs._ptr, name)) @@ -2028,8 +2267,8 @@ class Builder(llvm.Wrapper): def xor(self, lhs, rhs, name=""): return _make_value(self._ptr.CreateXor(lhs._ptr, rhs._ptr, name)) - def neg(self, val, name=""): - return _make_value(self._ptr.CreateNeg(val._ptr, name)) + def neg(self, val, name="", nuw=False, nsw=False): + return _make_value(self._ptr.CreateNeg(val._ptr, name, nuw, nsw)) def not_(self, val, name=""): return _make_value(self._ptr.CreateNot(val._ptr, name)) @@ -2037,7 +2276,6 @@ class Builder(llvm.Wrapper): # memory def malloc(self, ty, name=""): - context = api.llvm.getGlobalContext() allocsz = api.llvm.ConstantExpr.getSizeOf(ty._ptr) ity = allocsz.getType() malloc = api.llvm.CallInst.CreateMalloc(self.basic_block._ptr, @@ -2051,7 +2289,6 @@ class Builder(llvm.Wrapper): return _make_value(inst) def malloc_array(self, ty, size, name=""): - context = api.llvm.getGlobalContext() allocsz = api.llvm.ConstantExpr.getSizeOf(ty._ptr) ity = allocsz.getType() malloc = api.llvm.CallInst.CreateMalloc(self.basic_block._ptr, @@ -2064,12 +2301,13 @@ class Builder(llvm.Wrapper): inst = self._ptr.Insert(malloc, name) return _make_value(inst) - def alloca(self, ty, name=""): - intty = Type.int() - return _make_value(self._ptr.CreateAlloca(ty._ptr, None, name)) + def alloca(self, ty, size=None, name=""): + sizeptr = size._ptr if size else None + return _make_value(self._ptr.CreateAlloca(ty._ptr, sizeptr, name)) + @deprecated def alloca_array(self, ty, size, name=""): - return _make_value(self._ptr.CreateAlloca(ty._ptr, size._ptr, name)) + return self.alloca(ty, size, name=name) def free(self, ptr): free = api.llvm.CallInst.CreateFree(ptr._ptr, self.basic_block._ptr) @@ -2156,15 +2394,20 @@ class Builder(llvm.Wrapper): # misc def extract_value(self, retval, idx, name=""): - return _make_value(self._ptr.CreateExtractValue(retval._ptr, [idx], name)) + if not isinstance(idx, (tuple, list)): + idx = [idx] + return _make_value(self._ptr.CreateExtractValue(retval._ptr, idx, + name)) # obsolete synonym for extract_value getresult = extract_value def insert_value(self, retval, rhs, idx, name=""): + if not isinstance(idx, (tuple, list)): + idx = [idx] return _make_value(self._ptr.CreateInsertValue(retval._ptr, rhs._ptr, - [idx], + idx, name)) def phi(self, ty, name=""): @@ -2304,4 +2547,8 @@ if api.llvm.InitializeNativeTargetAsmPrinter(): # should this be an optional feature? # should user trigger the initialization? raise llvm.LLVMException("No native asm printer!?") - +if api.llvm.InitializeNativeTargetAsmParser(): + # required by MCJIT? + # should this be an optional feature? + # should user trigger the initialization? + raise llvm.LLVMException("No native asm parser!?") diff --git a/llvm/deprecated.py b/llvm/deprecated.py new file mode 100644 index 0000000..6db5fe7 --- /dev/null +++ b/llvm/deprecated.py @@ -0,0 +1,25 @@ +""" +Shameless borrowed from Smart_deprecation_warnings +https://wiki.python.org/moin/PythonDecoratorLibrary +""" + +import warnings +import functools + + +def deprecated(func): + """This is a decorator which can be used to mark functions + as deprecated. It will result in a warning being emitted + when the function is used.""" + + @functools.wraps(func) + def new_func(*args, **kwargs): + warnings.warn_explicit( + "Call to deprecated function %s." % (func.__name__,), + category=DeprecationWarning, + filename=func.func_code.co_filename, + lineno=func.func_code.co_firstlineno + 1 + ) + return func(*args, **kwargs) + + return new_func diff --git a/llvm/ee.py b/llvm/ee.py index e4ee4bc..f615b61 100644 --- a/llvm/ee.py +++ b/llvm/ee.py @@ -30,33 +30,23 @@ "Execution Engine and related classes." -from io import BytesIO -import contextlib +import sys import llvm from llvm import core from llvm.passes import TargetData, TargetTransformInfo from llvmpy import api, extra + #===----------------------------------------------------------------------=== -# Enumerations +# import items which were moved to target module #===----------------------------------------------------------------------=== +from llvm.target import (initialize_all, initialize_target, + print_registered_targets, get_host_cpu_name, get_default_triple, + TargetMachine, + BO_BIG_ENDIAN, BO_LITTLE_ENDIAN, + CM_DEFAULT, CM_JITDEFAULT, CM_SMALL, CM_KERNEL, CM_MEDIUM, CM_LARGE, + RELOC_DEFAULT, RELOC_STATIC, RELOC_PIC, RELOC_DYNAMIC_NO_PIC) -BO_BIG_ENDIAN = 0 -BO_LITTLE_ENDIAN = 1 - -# CodeModel -CM_DEFAULT = api.llvm.CodeModel.Model.Default -CM_JITDEFAULT = api.llvm.CodeModel.Model.JITDefault -CM_SMALL = api.llvm.CodeModel.Model.Small -CM_KERNEL = api.llvm.CodeModel.Model.Kernel -CM_MEDIUM = api.llvm.CodeModel.Model.Medium -CM_LARGE = api.llvm.CodeModel.Model.Large - -# Reloc -RELOC_DEFAULT = api.llvm.Reloc.Model.Default -RELOC_STATIC = api.llvm.Reloc.Model.Static -RELOC_PIC = api.llvm.Reloc.Model.PIC_ -RELOC_DYNAMIC_NO_PIC = api.llvm.Reloc.Model.DynamicNoPIC #===----------------------------------------------------------------------=== # Generic value @@ -150,9 +140,17 @@ class EngineBuilder(llvm.Wrapper): ''' if tm is not None: engine = self._ptr.create(tm._ptr) + elif (sys.platform.startswith('win32') and + getattr(self, '_use_mcjit', False)): + # force ELF generation on MCJIT on win32 + triple = get_default_triple() + tm = TargetMachine.new('%s-elf' % triple) + engine = self._ptr.create(tm._ptr) else: engine = self._ptr.create() - return ExecutionEngine(engine) + ee = ExecutionEngine(engine) + ee.finalize_object() # no effect for legacy JIT + return ee def select_target(self, *args): '''get the corresponding target machine @@ -167,6 +165,12 @@ class EngineBuilder(llvm.Wrapper): ptr = self._ptr.selectTarget() return TargetMachine(ptr) + def mcjit(self, enable): + '''Enable/disable MCJIT + ''' + self._ptr.setUseMCJIT(enable) + self._use_mcjit = True + return self #===----------------------------------------------------------------------=== # Execution engine @@ -188,6 +192,9 @@ class ExecutionEngine(llvm.Wrapper): ptr = self._ptr.runFunction(fn._ptr, list(map(lambda x: x._ptr, args))) return GenericValue(ptr) + def get_pointer_to_named_function(self, name, abort=True): + return self._ptr.getPointerToNamedFunction(name, abort) + def get_pointer_to_function(self, fn): return self._ptr.getPointerToFunction(fn._ptr) @@ -213,151 +220,45 @@ class ExecutionEngine(llvm.Wrapper): def remove_module(self, module): return self._ptr.removeModule(module._ptr) + def finalize_object(self): + return self._ptr.finalizeObject() + @property def target_data(self): ptr = self._ptr.getDataLayout() return TargetData(ptr) + #===----------------------------------------------------------------------=== -# Target machine +# Dynamic Library #===----------------------------------------------------------------------=== -def initialize_target(target, noraise=False): - """Initialize target by name. - It is safe to initialize the same target multiple times. +def dylib_add_symbol(name, ptr): + api.llvm.sys.DynamicLibrary.AddSymbol(name, ptr) + +def dylib_address_of_symbol(name): + return api.llvm.sys.DynamicLibrary.SearchForAddressOfSymbol(name) + +def dylib_import_library(filename): + """Permanently import a dynamic library. + + Returns a DynamicLibrary object + + Raises RuntimeError """ - prefix = 'LLVMInitialize' - postfixes = ['Target', 'TargetInfo', 'TargetMC', 'AsmPrinter'] - try: - for postfix in postfixes: - getattr(api, '%s%s%s' % (prefix, target, postfix))() - except AttributeError: - if noraise: - return False - else: - raise - else: - return True + return DynamicLibrary(filename) -def print_registered_targets(): - ''' - Note: print directly to stdout - ''' - api.llvm.TargetRegistry.printRegisteredTargetsForVersion() - -def get_host_cpu_name(): - '''return the string name of the host CPU - ''' - return api.llvm.sys.getHostCPUName() - -def get_default_triple(): - '''return the target triple of the host in str-rep - ''' - return api.llvm.sys.getDefaultTargetTriple() - - -class TargetMachine(llvm.Wrapper): - - @staticmethod - def new(triple='', cpu='', features='', opt=2, cm=CM_DEFAULT, - reloc=RELOC_DEFAULT): - if not triple: - triple = get_default_triple() - if not cpu: - cpu = get_host_cpu_name() - with contextlib.closing(BytesIO()) as error: - target = api.llvm.TargetRegistry.lookupTarget(triple, error) - if not target: - raise llvm.LLVMException(error) - if not target.hasTargetMachine(): - raise llvm.LLVMException(target, "No target machine.") - target_options = api.llvm.TargetOptions.new() - tm = target.createTargetMachine(triple, cpu, features, - target_options, - reloc, cm, opt) - if not tm: - raise llvm.LLVMException("Cannot create target machine") - return TargetMachine(tm) - - @staticmethod - def lookup(arch, cpu='', features='', opt=2, cm=CM_DEFAULT, - reloc=RELOC_DEFAULT): - '''create a targetmachine given an architecture name - - For a list of architectures, - use: `llc -help` - - For a list of available CPUs, - use: `llvm-as < /dev/null | llc -march=xyz -mcpu=help` - - For a list of available attributes (features), - use: `llvm-as < /dev/null | llc -march=xyz -mattr=help` - ''' - triple = api.llvm.Triple.new() - with contextlib.closing(BytesIO()) as error: - target = api.llvm.TargetRegistry.lookupTarget(arch, triple, error) - if not target: - raise llvm.LLVMException(error.getvalue()) - if not target.hasTargetMachine(): - raise llvm.LLVMException(target, "No target machine.") - target_options = api.llvm.TargetOptions.new() - tm = target.createTargetMachine(str(triple), cpu, features, - target_options, - reloc, cm, opt) - if not tm: - raise llvm.LLVMException("Cannot create target machine") - return TargetMachine(tm) - - def _emit_file(self, module, cgft): - pm = api.llvm.PassManager.new() - os = extra.make_raw_ostream_for_printing() - pm.add(api.llvm.DataLayout.new(str(self.target_data))) - failed = self._ptr.addPassesToEmitFile(pm, os, cgft) - pm.run(module) - - - CGFT = api.llvm.TargetMachine.CodeGenFileType - if cgft == CGFT.CGFT_ObjectFile: - return os.bytes() - else: - return os.str() - - def emit_assembly(self, module): - '''returns byte string of the module as assembly code of the target machine - ''' - CGFT = api.llvm.TargetMachine.CodeGenFileType - return self._emit_file(module._ptr, CGFT.CGFT_AssemblyFile) - - def emit_object(self, module): - '''returns byte string of the module as native code of the target machine - ''' - CGFT = api.llvm.TargetMachine.CodeGenFileType - return self._emit_file(module._ptr, CGFT.CGFT_ObjectFile) - - @property - def target_data(self): - '''get target data of this machine - ''' - return TargetData(self._ptr.getDataLayout()) - - @property - def target_name(self): - return self._ptr.getTarget().getName() - - @property - def target_short_description(self): - return self._ptr.getTarget().getShortDescription() - - @property - def triple(self): - return self._ptr.getTargetTriple() - - @property - def cpu(self): - return self._ptr.getTargetCPU() - - @property - def feature_string(self): - return self._ptr.getTargetFeatureString() +class DynamicLibrary(object): + def __init__(self, filename): + """ + Raises RuntimeError + """ + self._ptr = api.llvm.sys.DynamicLibrary.getPermanentLibrary( + filename) + def get_address_of_symbol(self, symbol): + """ + Get the address of `symbol` (str) as integer + """ + return self._ptr.getAddressOfSymbol(symbol) diff --git a/llvm/llrt.py b/llvm/llrt.py new file mode 100644 index 0000000..c704a66 --- /dev/null +++ b/llvm/llrt.py @@ -0,0 +1,79 @@ +import os +import llvm.core as lc +import llvm.passes as lp +import llvm.ee as le + +def replace_divmod64(lfunc): + '''Replaces all 64-bit integer division (sdiv, udiv) and modulo (srem, urem) + ''' + int64 = lc.Type.int(64) + int64ptr = lc.Type.pointer(lc.Type.int(64)) + + functy = lc.Type.function(int64, [int64, int64]) + udiv64 = lfunc.module.get_or_insert_function(functy, '__llrt_udiv64') + sdiv64 = lfunc.module.get_or_insert_function(functy, '__llrt_sdiv64') + umod64 = lfunc.module.get_or_insert_function(functy, '__llrt_umod64') + smod64 = lfunc.module.get_or_insert_function(functy, '__llrt_smod64') + + builder = lc.Builder.new(lfunc.entry_basic_block) + for bb in lfunc.basic_blocks: + for inst in bb.instructions: + if inst.opcode_name == 'sdiv' and inst.type == int64: + _replace_with(builder, inst, sdiv64) + elif inst.opcode_name == 'udiv' and inst.type == int64: + _replace_with(builder, inst, udiv64) + elif inst.opcode_name == 'srem' and inst.type == int64: + _replace_with(builder, inst, smod64) + elif inst.opcode_name == 'urem' and inst.type == int64: + _replace_with(builder, inst, umod64) + +def _replace_with(builder, inst, func): + '''Replace instruction with a call to the function with the same operands + as arguments. + ''' + builder.position_before(inst) + replacement = builder.call(func, inst.operands) + inst.replace_all_uses_with(replacement._ptr) + inst.erase_from_parent() + +def load(arch): + '''Load the LLRT module corresponding to the given architecture + Creates a new module and optimizes it using the information from + the host machine. + ''' + if arch != 'x86_64': + arch = 'x86' + path = os.path.join(os.path.dirname(__file__), 'llrt', 'llrt_%s.ll' % arch) + with open(path) as fin: + lib = lc.Module.from_assembly(fin) + + # run passes to optimize + tm = le.TargetMachine.new() + pms = lp.build_pass_managers(tm, opt=3, fpm=False) + pms.pm.run(lib) + return lib + +class LLRT(object): + def __init__(self): + arch = le.get_default_triple().split('-', 1)[0] + self.module = load(arch) + self.engine = le.EngineBuilder.new(self.module).opt(3).create() + self.installed_symbols = set() + + def install_symbols(self): + '''Bind all the external symbols to the global symbol map. + Any future reference to these symbols will be automatically resolved + by LLVM. + ''' + for lfunc in self.module.functions: + if lfunc.linkage == lc.LINKAGE_EXTERNAL: + mangled = '__llrt_' + lfunc.name + self.installed_symbols.add(mangled) + ptr = self.engine.get_pointer_to_function(lfunc) + le.dylib_add_symbol(mangled, ptr) + + def uninstall_symbols(self): + for sym in self.installed_symbols: + le.dylib_add_symbol(sym, 0) + + diff --git a/llvm/llrt/llrt_x86.ll b/llvm/llrt/llrt_x86.ll new file mode 100644 index 0000000..5096214 --- /dev/null +++ b/llvm/llrt/llrt_x86.ll @@ -0,0 +1,371 @@ +; ModuleID = 'udivmod64_x86.bc' +target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:128:128-n8:16:32-S128" + +%struct.div_state_ = type { i64, i64 } + +define i64 @udivmod64(i64 %dividend, i64 %divisor, i64* %remainder) nounwind ssp { + %1 = alloca i64, align 4 + %2 = alloca i64, align 8 + %3 = alloca i64, align 8 + %4 = alloca i64*, align 4 + %state = alloca %struct.div_state_, align 4 + %quotient = alloca i64, align 8 + %i = alloca i32, align 4 + %skipahead = alloca i32, align 4 + store i64 %dividend, i64* %2, align 8 + store i64 %divisor, i64* %3, align 8 + store i64* %remainder, i64** %4, align 4 + %5 = getelementptr inbounds %struct.div_state_* %state, i32 0, i32 0 + store i64 0, i64* %5, align 4 + %6 = getelementptr inbounds %struct.div_state_* %state, i32 0, i32 1 + %7 = load i64* %2, align 8 + store i64 %7, i64* %6, align 4 + store i64 0, i64* %quotient, align 8 + %8 = load i64* %3, align 8 + %9 = icmp eq i64 %8, 0 + br i1 %9, label %10, label %11 + +;