diff options
Diffstat (limited to 'bindings/python')
-rw-r--r-- | bindings/python/llvm/bit_reader.py | 31 | ||||
-rw-r--r-- | bindings/python/llvm/core.py | 442 | ||||
-rw-r--r-- | bindings/python/llvm/disassembler.py | 29 | ||||
-rw-r--r-- | bindings/python/llvm/tests/base.py | 6 | ||||
-rw-r--r-- | bindings/python/llvm/tests/test.bc | bin | 0 -> 1344 bytes | |||
-rw-r--r-- | bindings/python/llvm/tests/test_bitreader.py | 15 | ||||
-rw-r--r-- | bindings/python/llvm/tests/test_core.py | 109 | ||||
-rw-r--r-- | bindings/python/llvm/tests/test_disassembler.py | 4 | ||||
-rw-r--r-- | bindings/python/llvm/tests/test_file | 1 |
9 files changed, 634 insertions, 3 deletions
diff --git a/bindings/python/llvm/bit_reader.py b/bindings/python/llvm/bit_reader.py new file mode 100644 index 0000000..5bf5e22 --- /dev/null +++ b/bindings/python/llvm/bit_reader.py @@ -0,0 +1,31 @@ + +from .common import LLVMObject +from .common import c_object_p +from .common import get_library +from . import enumerations +from .core import MemoryBuffer +from .core import Module +from .core import OpCode +from ctypes import POINTER +from ctypes import byref +from ctypes import c_char_p +from ctypes import cast +__all__ = ['parse_bitcode'] +lib = get_library() + +def parse_bitcode(mem_buffer): + """Input is .core.MemoryBuffer""" + module = c_object_p() + out = c_char_p(None) + result = lib.LLVMParseBitcode(mem_buffer, byref(module), byref(out)) + if result: + raise RuntimeError('LLVM Error: %s' % out.value) + m = Module(module) + m.take_ownership(mem_buffer) + return m + +def register_library(library): + library.LLVMParseBitcode.argtypes = [MemoryBuffer, POINTER(c_object_p), POINTER(c_char_p)] + library.LLVMParseBitcode.restype = bool + +register_library(lib) diff --git a/bindings/python/llvm/core.py b/bindings/python/llvm/core.py index 6756637..ebf57d4 100644 --- a/bindings/python/llvm/core.py +++ b/bindings/python/llvm/core.py @@ -16,10 +16,19 @@ from . import enumerations from ctypes import POINTER from ctypes import byref from ctypes import c_char_p +from ctypes import c_uint __all__ = [ "lib", + "OpCode", "MemoryBuffer", + "Module", + "Value", + "Function", + "BasicBlock", + "Instruction", + "Context", + "PassRegistry" ] lib = get_library() @@ -83,16 +92,449 @@ class MemoryBuffer(LLVMObject): LLVMObject.__init__(self, memory, disposer=lib.LLVMDisposeMemoryBuffer) + def __len__(self): + return lib.LLVMGetBufferSize(self) + +class Value(LLVMObject): + + def __init__(self, value): + LLVMObject.__init__(self, value) + + @property + def name(self): + return lib.LLVMGetValueName(self) + + def dump(self): + lib.LLVMDumpValue(self) + + def get_operand(self, i): + return Value(lib.LLVMGetOperand(self, i)) + + def set_operand(self, i, v): + return lib.LLVMSetOperand(self, i, v) + + def __len__(self): + return lib.LLVMGetNumOperands(self) + +class Module(LLVMObject): + """Represents the top-level structure of an llvm program in an opaque object.""" + + def __init__(self, module, name=None, context=None): + LLVMObject.__init__(self, module, disposer=lib.LLVMDisposeModule) + + @classmethod + def CreateWithName(cls, module_id): + m = Module(lib.LLVMModuleCreateWithName(module_id)) + c = Context.GetGlobalContext().take_ownership(m) + return m + + @property + def datalayout(self): + return lib.LLVMGetDataLayout(self) + + @datalayout.setter + def datalayout(self, new_data_layout): + """new_data_layout is a string.""" + lib.LLVMSetDataLayout(self, new_data_layout) + + @property + def target(self): + return lib.LLVMGetTarget(self) + + @target.setter + def target(self, new_target): + """new_target is a string.""" + lib.LLVMSetTarget(self, new_target) + + def dump(self): + lib.LLVMDumpModule(self) + + class __function_iterator(object): + def __init__(self, module, reverse=False): + self.module = module + self.reverse = reverse + if self.reverse: + self.function = self.module.last + else: + self.function = self.module.first + + def __iter__(self): + return self + + def next(self): + if not isinstance(self.function, Function): + raise StopIteration("") + result = self.function + if self.reverse: + self.function = self.function.prev + else: + self.function = self.function.next + return result + + def __iter__(self): + return Module.__function_iterator(self) + + def __reversed__(self): + return Module.__function_iterator(self, reverse=True) + + @property + def first(self): + return Function(lib.LLVMGetFirstFunction(self)) + + @property + def last(self): + return Function(lib.LLVMGetLastFunction(self)) + + def print_module_to_file(self, filename): + out = c_char_p(None) + # Result is inverted so 0 means everything was ok. + result = lib.LLVMPrintModuleToFile(self, filename, byref(out)) + if result: + raise RuntimeError("LLVM Error: %s" % out.value) + +class Function(Value): + + def __init__(self, value): + Value.__init__(self, value) + + @property + def next(self): + f = lib.LLVMGetNextFunction(self) + return f and Function(f) + + @property + def prev(self): + f = lib.LLVMGetPreviousFunction(self) + return f and Function(f) + + @property + def first(self): + b = lib.LLVMGetFirstBasicBlock(self) + return b and BasicBlock(b) + + @property + def last(self): + b = lib.LLVMGetLastBasicBlock(self) + return b and BasicBlock(b) + + class __bb_iterator(object): + def __init__(self, function, reverse=False): + self.function = function + self.reverse = reverse + if self.reverse: + self.bb = function.last + else: + self.bb = function.first + + def __iter__(self): + return self + + def next(self): + if not isinstance(self.bb, BasicBlock): + raise StopIteration("") + result = self.bb + if self.reverse: + self.bb = self.bb.prev + else: + self.bb = self.bb.next + return result + + def __iter__(self): + return Function.__bb_iterator(self) + + def __reversed__(self): + return Function.__bb_iterator(self, reverse=True) + + def __len__(self): + return lib.LLVMCountBasicBlocks(self) + +class BasicBlock(LLVMObject): + + def __init__(self, value): + LLVMObject.__init__(self, value) + + @property + def next(self): + b = lib.LLVMGetNextBasicBlock(self) + return b and BasicBlock(b) + + @property + def prev(self): + b = lib.LLVMGetPreviousBasicBlock(self) + return b and BasicBlock(b) + + @property + def first(self): + i = lib.LLVMGetFirstInstruction(self) + return i and Instruction(i) + + @property + def last(self): + i = lib.LLVMGetLastInstruction(self) + return i and Instruction(i) + + def __as_value(self): + return Value(lib.LLVMBasicBlockAsValue(self)) + + @property + def name(self): + return lib.LLVMGetValueName(self.__as_value()) + + def dump(self): + lib.LLVMDumpValue(self.__as_value()) + + def get_operand(self, i): + return Value(lib.LLVMGetOperand(self.__as_value(), + i)) + + def set_operand(self, i, v): + return lib.LLVMSetOperand(self.__as_value(), + i, v) + + def __len__(self): + return lib.LLVMGetNumOperands(self.__as_value()) + + class __inst_iterator(object): + def __init__(self, bb, reverse=False): + self.bb = bb + self.reverse = reverse + if self.reverse: + self.inst = self.bb.last + else: + self.inst = self.bb.first + + def __iter__(self): + return self + + def next(self): + if not isinstance(self.inst, Instruction): + raise StopIteration("") + result = self.inst + if self.reverse: + self.inst = self.inst.prev + else: + self.inst = self.inst.next + return result + + def __iter__(self): + return BasicBlock.__inst_iterator(self) + + def __reversed__(self): + return BasicBlock.__inst_iterator(self, reverse=True) + + +class Instruction(Value): + + def __init__(self, value): + Value.__init__(self, value) + + @property + def next(self): + i = lib.LLVMGetNextInstruction(self) + return i and Instruction(i) + + @property + def prev(self): + i = lib.LLVMGetPreviousInstruction(self) + return i and Instruction(i) + + @property + def opcode(self): + return OpCode.from_value(lib.LLVMGetInstructionOpcode(self)) + +class Context(LLVMObject): + + def __init__(self, context=None): + if context is None: + context = lib.LLVMContextCreate() + LLVMObject.__init__(self, context, disposer=lib.LLVMContextDispose) + else: + LLVMObject.__init__(self, context) + + @classmethod + def GetGlobalContext(cls): + return Context(lib.LLVMGetGlobalContext()) + +class PassRegistry(LLVMObject): + """Represents an opaque pass registry object.""" + + def __init__(self): + LLVMObject.__init__(self, + lib.LLVMGetGlobalPassRegistry()) + def register_library(library): + # Initialization/Shutdown declarations. + library.LLVMInitializeCore.argtypes = [PassRegistry] + library.LLVMInitializeCore.restype = None + + library.LLVMInitializeTransformUtils.argtypes = [PassRegistry] + library.LLVMInitializeTransformUtils.restype = None + + library.LLVMInitializeScalarOpts.argtypes = [PassRegistry] + library.LLVMInitializeScalarOpts.restype = None + + library.LLVMInitializeObjCARCOpts.argtypes = [PassRegistry] + library.LLVMInitializeObjCARCOpts.restype = None + + library.LLVMInitializeVectorization.argtypes = [PassRegistry] + library.LLVMInitializeVectorization.restype = None + + library.LLVMInitializeInstCombine.argtypes = [PassRegistry] + library.LLVMInitializeInstCombine.restype = None + + library.LLVMInitializeIPO.argtypes = [PassRegistry] + library.LLVMInitializeIPO.restype = None + + library.LLVMInitializeInstrumentation.argtypes = [PassRegistry] + library.LLVMInitializeInstrumentation.restype = None + + library.LLVMInitializeAnalysis.argtypes = [PassRegistry] + library.LLVMInitializeAnalysis.restype = None + + library.LLVMInitializeIPA.argtypes = [PassRegistry] + library.LLVMInitializeIPA.restype = None + + library.LLVMInitializeCodeGen.argtypes = [PassRegistry] + library.LLVMInitializeCodeGen.restype = None + + library.LLVMInitializeTarget.argtypes = [PassRegistry] + library.LLVMInitializeTarget.restype = None + + library.LLVMShutdown.argtypes = [] + library.LLVMShutdown.restype = None + + # Pass Registry declarations. + library.LLVMGetGlobalPassRegistry.argtypes = [] + library.LLVMGetGlobalPassRegistry.restype = c_object_p + + # Context declarations. + library.LLVMContextCreate.argtypes = [] + library.LLVMContextCreate.restype = c_object_p + + library.LLVMContextDispose.argtypes = [Context] + library.LLVMContextDispose.restype = None + + library.LLVMGetGlobalContext.argtypes = [] + library.LLVMGetGlobalContext.restype = c_object_p + + # Memory buffer declarations library.LLVMCreateMemoryBufferWithContentsOfFile.argtypes = [c_char_p, POINTER(c_object_p), POINTER(c_char_p)] library.LLVMCreateMemoryBufferWithContentsOfFile.restype = bool + library.LLVMGetBufferSize.argtypes = [MemoryBuffer] + library.LLVMDisposeMemoryBuffer.argtypes = [MemoryBuffer] + # Module declarations + library.LLVMModuleCreateWithName.argtypes = [c_char_p] + library.LLVMModuleCreateWithName.restype = c_object_p + + library.LLVMDisposeModule.argtypes = [Module] + library.LLVMDisposeModule.restype = None + + library.LLVMGetDataLayout.argtypes = [Module] + library.LLVMGetDataLayout.restype = c_char_p + + library.LLVMSetDataLayout.argtypes = [Module, c_char_p] + library.LLVMSetDataLayout.restype = None + + library.LLVMGetTarget.argtypes = [Module] + library.LLVMGetTarget.restype = c_char_p + + library.LLVMSetTarget.argtypes = [Module, c_char_p] + library.LLVMSetTarget.restype = None + + library.LLVMDumpModule.argtypes = [Module] + library.LLVMDumpModule.restype = None + + library.LLVMPrintModuleToFile.argtypes = [Module, c_char_p, + POINTER(c_char_p)] + library.LLVMPrintModuleToFile.restype = bool + + library.LLVMGetFirstFunction.argtypes = [Module] + library.LLVMGetFirstFunction.restype = c_object_p + + library.LLVMGetLastFunction.argtypes = [Module] + library.LLVMGetLastFunction.restype = c_object_p + + library.LLVMGetNextFunction.argtypes = [Function] + library.LLVMGetNextFunction.restype = c_object_p + + library.LLVMGetPreviousFunction.argtypes = [Function] + library.LLVMGetPreviousFunction.restype = c_object_p + + # Value declarations. + library.LLVMGetValueName.argtypes = [Value] + library.LLVMGetValueName.restype = c_char_p + + library.LLVMDumpValue.argtypes = [Value] + library.LLVMDumpValue.restype = None + + library.LLVMGetOperand.argtypes = [Value, c_uint] + library.LLVMGetOperand.restype = c_object_p + + library.LLVMSetOperand.argtypes = [Value, Value, c_uint] + library.LLVMSetOperand.restype = None + + library.LLVMGetNumOperands.argtypes = [Value] + library.LLVMGetNumOperands.restype = c_uint + + # Basic Block Declarations. + library.LLVMGetFirstBasicBlock.argtypes = [Function] + library.LLVMGetFirstBasicBlock.restype = c_object_p + + library.LLVMGetLastBasicBlock.argtypes = [Function] + library.LLVMGetLastBasicBlock.restype = c_object_p + + library.LLVMGetNextBasicBlock.argtypes = [BasicBlock] + library.LLVMGetNextBasicBlock.restype = c_object_p + + library.LLVMGetPreviousBasicBlock.argtypes = [BasicBlock] + library.LLVMGetPreviousBasicBlock.restype = c_object_p + + library.LLVMGetFirstInstruction.argtypes = [BasicBlock] + library.LLVMGetFirstInstruction.restype = c_object_p + + library.LLVMGetLastInstruction.argtypes = [BasicBlock] + library.LLVMGetLastInstruction.restype = c_object_p + + library.LLVMBasicBlockAsValue.argtypes = [BasicBlock] + library.LLVMBasicBlockAsValue.restype = c_object_p + + library.LLVMCountBasicBlocks.argtypes = [Function] + library.LLVMCountBasicBlocks.restype = c_uint + + # Instruction Declarations. + library.LLVMGetNextInstruction.argtypes = [Instruction] + library.LLVMGetNextInstruction.restype = c_object_p + + library.LLVMGetPreviousInstruction.argtypes = [Instruction] + library.LLVMGetPreviousInstruction.restype = c_object_p + + library.LLVMGetInstructionOpcode.argtypes = [Instruction] + library.LLVMGetInstructionOpcode.restype = c_uint + def register_enumerations(): for name, value in enumerations.OpCodes: OpCode.register(name, value) +def initialize_llvm(): + c = Context.GetGlobalContext() + p = PassRegistry() + lib.LLVMInitializeCore(p) + lib.LLVMInitializeTransformUtils(p) + lib.LLVMInitializeScalarOpts(p) + lib.LLVMInitializeObjCARCOpts(p) + lib.LLVMInitializeVectorization(p) + lib.LLVMInitializeInstCombine(p) + lib.LLVMInitializeIPO(p) + lib.LLVMInitializeInstrumentation(p) + lib.LLVMInitializeAnalysis(p) + lib.LLVMInitializeIPA(p) + lib.LLVMInitializeCodeGen(p) + lib.LLVMInitializeTarget(p) + register_library(lib) register_enumerations() +initialize_llvm() diff --git a/bindings/python/llvm/disassembler.py b/bindings/python/llvm/disassembler.py index dcef9ac..f2df275 100644 --- a/bindings/python/llvm/disassembler.py +++ b/bindings/python/llvm/disassembler.py @@ -10,7 +10,6 @@ from ctypes import CFUNCTYPE from ctypes import POINTER from ctypes import addressof -from ctypes import byref from ctypes import c_byte from ctypes import c_char_p from ctypes import c_int @@ -34,6 +33,29 @@ callbacks = {} # Constants for set_options Option_UseMarkup = 1 + + +_initialized = False +_targets = ['AArch64', 'ARM', 'Hexagon', 'MSP430', 'Mips', 'NVPTX', 'PowerPC', 'R600', 'Sparc', 'SystemZ', 'X86', 'XCore'] +def _ensure_initialized(): + global _initialized + if not _initialized: + # Here one would want to call the functions + # LLVMInitializeAll{TargetInfo,TargetMC,Disassembler}s, but + # unfortunately they are only defined as static inline + # functions in the header files of llvm-c, so they don't exist + # as symbols in the shared library. + # So until that is fixed use this hack to initialize them all + for tgt in _targets: + for initializer in ("TargetInfo", "TargetMC", "Disassembler"): + try: + f = getattr(lib, "LLVMInitialize" + tgt + initializer) + except AttributeError: + continue + f() + _initialized = True + + class Disassembler(LLVMObject): """Represents a disassembler instance. @@ -48,9 +70,12 @@ class Disassembler(LLVMObject): The triple argument is the triple to create the disassembler for. This is something like 'i386-apple-darwin9'. """ + + _ensure_initialized() + ptr = lib.LLVMCreateDisasm(c_char_p(triple), c_void_p(None), c_int(0), callbacks['op_info'](0), callbacks['symbol_lookup'](0)) - if not ptr.contents: + if not ptr: raise Exception('Could not obtain disassembler for triple: %s' % triple) diff --git a/bindings/python/llvm/tests/base.py b/bindings/python/llvm/tests/base.py index ff9eb2f..194f1a4 100644 --- a/bindings/python/llvm/tests/base.py +++ b/bindings/python/llvm/tests/base.py @@ -30,3 +30,9 @@ class TestBase(unittest.TestCase): raise Exception('No suitable test binaries available!') get_test_binary.__test__ = False + + def get_test_file(self): + return os.path.join(os.path.dirname(os.path.abspath(__file__)), "test_file") + + def get_test_bc(self): + return os.path.join(os.path.dirname(os.path.abspath(__file__)), "test.bc") diff --git a/bindings/python/llvm/tests/test.bc b/bindings/python/llvm/tests/test.bc Binary files differnew file mode 100644 index 0000000..8d3d28f --- /dev/null +++ b/bindings/python/llvm/tests/test.bc diff --git a/bindings/python/llvm/tests/test_bitreader.py b/bindings/python/llvm/tests/test_bitreader.py new file mode 100644 index 0000000..d585009 --- /dev/null +++ b/bindings/python/llvm/tests/test_bitreader.py @@ -0,0 +1,15 @@ +from .base import TestBase +from ..core import OpCode +from ..core import MemoryBuffer +from ..core import PassRegistry +from ..core import Context +from ..core import Module +from ..bit_reader import parse_bitcode + +class TestBitReader(TestBase): + + def test_parse_bitcode(self): + source = self.get_test_bc() + m = parse_bitcode(MemoryBuffer(filename=source)) + print m.target + print m.datalayout diff --git a/bindings/python/llvm/tests/test_core.py b/bindings/python/llvm/tests/test_core.py index 545abc8..63f84c8 100644 --- a/bindings/python/llvm/tests/test_core.py +++ b/bindings/python/llvm/tests/test_core.py @@ -1,6 +1,10 @@ from .base import TestBase from ..core import OpCode from ..core import MemoryBuffer +from ..core import PassRegistry +from ..core import Context +from ..core import Module +from ..bit_reader import parse_bitcode class TestCore(TestBase): def test_opcode(self): @@ -13,7 +17,7 @@ class TestCore(TestBase): self.assertEqual(op, OpCode.Ret) def test_memory_buffer_create_from_file(self): - source = self.get_test_binary() + source = self.get_test_file() MemoryBuffer(filename=source) @@ -21,3 +25,106 @@ class TestCore(TestBase): with self.assertRaises(Exception): MemoryBuffer(filename="/hopefully/this/path/doesnt/exist") + def test_memory_buffer_len(self): + source = self.get_test_file() + m = MemoryBuffer(filename=source) + self.assertEqual(len(m), 50) + + def test_create_passregistry(self): + PassRegistry() + + def test_create_context(self): + Context.GetGlobalContext() + + def test_create_module_with_name(self): + # Make sure we can not create a module without a LLVMModuleRef. + with self.assertRaises(TypeError): + m = Module() + m = Module.CreateWithName("test-module") + + def test_module_getset_datalayout(self): + m = Module.CreateWithName("test-module") + dl = "e-p:32:32:32-i1:8:32-i8:8:32-i16:16:32-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:32:64-v128:32:128-a0:0:32-n32-S32" + m.datalayout = dl + self.assertEqual(m.datalayout, dl) + + def test_module_getset_target(self): + m = Module.CreateWithName("test-module") + target = "thumbv7-apple-ios5.0.0" + m.target = target + self.assertEqual(m.target, target) + + def test_module_print_module_to_file(self): + m = Module.CreateWithName("test") + dl = "e-p:32:32:32-i1:8:32-i8:8:32-i16:16:32-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:32:64-v128:32:128-a0:0:32-n32-S32" + m.datalayout = dl + target = "thumbv7-apple-ios5.0.0" + m.target = target + m.print_module_to_file("test2.ll") + + def test_module_function_iteration(self): + m = parse_bitcode(MemoryBuffer(filename=self.get_test_bc())) + i = 0 + functions = ["f", "f2", "f3", "f4", "f5", "f6", "g1", "g2", "h1", "h2", + "h3"] + # Forward + for f in m: + self.assertEqual(f.name, functions[i]) + f.dump() + i += 1 + # Backwards + for f in reversed(m): + i -= 1 + self.assertEqual(f.name, functions[i]) + f.dump() + + def test_function_basicblock_iteration(self): + m = parse_bitcode(MemoryBuffer(filename=self.get_test_bc())) + i = 0 + + bb_list = ['b1', 'b2', 'end'] + + f = m.first + while f.name != "f6": + f = f.next + + # Forward + for bb in f: + self.assertEqual(bb.name, bb_list[i]) + bb.dump() + i += 1 + + # Backwards + for bb in reversed(f): + i -= 1 + self.assertEqual(bb.name, bb_list[i]) + bb.dump() + + def test_basicblock_instruction_iteration(self): + m = parse_bitcode(MemoryBuffer(filename=self.get_test_bc())) + i = 0 + + inst_list = [('arg1', OpCode.ExtractValue), + ('arg2', OpCode.ExtractValue), + ('', OpCode.Call), + ('', OpCode.Ret)] + + bb = m.first.first + + # Forward + for inst in bb: + self.assertEqual(inst.name, inst_list[i][0]) + self.assertEqual(inst.opcode, inst_list[i][1]) + for op in range(len(inst)): + o = inst.get_operand(op) + print o.name + o.dump() + inst.dump() + i += 1 + + # Backwards + for inst in reversed(bb): + i -= 1 + self.assertEqual(inst.name, inst_list[i][0]) + self.assertEqual(inst.opcode, inst_list[i][1]) + inst.dump() diff --git a/bindings/python/llvm/tests/test_disassembler.py b/bindings/python/llvm/tests/test_disassembler.py index 46d12f7..e960dc0 100644 --- a/bindings/python/llvm/tests/test_disassembler.py +++ b/bindings/python/llvm/tests/test_disassembler.py @@ -16,6 +16,10 @@ class TestDisassembler(TestBase): self.assertEqual(count, 3) self.assertEqual(s, '\tjcxz\t-127') + def test_nonexistant_triple(self): + with self.assertRaisesRegexp(Exception, "Could not obtain disassembler for triple"): + Disassembler("nonexistant-triple-raises") + def test_get_instructions(self): sequence = '\x67\xe3\x81\x01\xc7' # jcxz -127; addl %eax, %edi diff --git a/bindings/python/llvm/tests/test_file b/bindings/python/llvm/tests/test_file new file mode 100644 index 0000000..6c9b038 --- /dev/null +++ b/bindings/python/llvm/tests/test_file @@ -0,0 +1 @@ +I,"cAGxqԐdvl\L>g>``wɩ
\ No newline at end of file |