266 lines
8.4 KiB
Python
266 lines
8.4 KiB
Python
import llvm
|
|
from llvmpy import api, extra
|
|
from io import BytesIO
|
|
import contextlib
|
|
from llvm.passes import TargetData
|
|
|
|
#===----------------------------------------------------------------------===
|
|
# Enumerations
|
|
#===----------------------------------------------------------------------===
|
|
|
|
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
|
|
|
|
def initialize_all():
|
|
api.llvm.InitializeAllTargets()
|
|
api.llvm.InitializeAllTargetInfos()
|
|
api.llvm.InitializeAllTargetMCs()
|
|
api.llvm.InitializeAllAsmPrinters()
|
|
api.llvm.InitializeAllDisassemblers()
|
|
api.llvm.InitializeAllAsmParsers()
|
|
|
|
def initialize_target(target, noraise=False):
|
|
"""Initialize target by name.
|
|
It is safe to initialize the same target multiple times.
|
|
"""
|
|
prefix = 'LLVMInitialize'
|
|
postfixes = ['Target', 'TargetInfo', 'TargetMC', 'AsmPrinter', 'AsmParser']
|
|
try:
|
|
for postfix in postfixes:
|
|
getattr(api, '%s%s%s' % (prefix, target, postfix))()
|
|
except AttributeError:
|
|
if noraise:
|
|
return False
|
|
else:
|
|
raise
|
|
else:
|
|
return True
|
|
|
|
|
|
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.getvalue())
|
|
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)
|
|
|
|
@staticmethod
|
|
def x86():
|
|
return TargetMachine.lookup('x86')
|
|
|
|
@staticmethod
|
|
def x86_64():
|
|
return TargetMachine.lookup('x86-64')
|
|
|
|
@staticmethod
|
|
def arm():
|
|
return TargetMachine.lookup('arm')
|
|
|
|
@staticmethod
|
|
def thumb():
|
|
return TargetMachine.lookup('thumb')
|
|
|
|
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()
|
|
|
|
@property
|
|
def target(self):
|
|
return self._ptr.getTarget()
|
|
|
|
if llvm.version >= (3, 3):
|
|
def add_analysis_passes(self, pm):
|
|
self._ptr.addAnalysisPasses(pm._ptr)
|
|
|
|
if llvm.version >= (3, 4):
|
|
@property
|
|
def reg_info(self):
|
|
mri = self._ptr.getRegisterInfo()
|
|
if not mri:
|
|
raise llvm.LLVMException("no reg info for this machine")
|
|
|
|
return mri
|
|
|
|
@property
|
|
def subtarget_info(self):
|
|
sti = self._ptr.getSubtargetImpl()
|
|
if not sti:
|
|
raise llvm.LLVMException("no subtarget info for this machine")
|
|
|
|
return sti
|
|
|
|
@property
|
|
def asm_info(self):
|
|
ai = self._ptr.getMCAsmInfo()
|
|
if not ai:
|
|
raise llvm.LLVMException("no asm info for this machine")
|
|
|
|
return ai
|
|
|
|
@property
|
|
def instr_info(self):
|
|
ii = self._ptr.getInstrInfo()
|
|
if not ii:
|
|
raise llvm.LLVMException("no instr info for this machine")
|
|
|
|
return ii
|
|
|
|
@property
|
|
def instr_analysis(self):
|
|
if not getattr(self, '_mia', False):
|
|
self._mia = self.target.createMCInstrAnalysis(self.instr_info)
|
|
if not self._mia:
|
|
raise llvm.LLVMException("no instr analysis for this machine")
|
|
|
|
return self._mia
|
|
|
|
@property
|
|
def disassembler(self):
|
|
if not getattr(self, '_dasm', False):
|
|
self._dasm = self.target.createMCDisassembler(self.subtarget_info)
|
|
if not self._dasm:
|
|
raise llvm.LLVMException("no disassembler for this machine")
|
|
|
|
return self._dasm
|
|
|
|
@property
|
|
def inst_printer(self):
|
|
if not getattr(self, '_mip', False):
|
|
self._mip = self.target.createMCInstPrinter(
|
|
self.asm_info.getAssemblerDialect(),
|
|
self.asm_info,
|
|
self.instr_info,
|
|
self.reg_info,
|
|
self.subtarget_info
|
|
)
|
|
if not self._mip:
|
|
raise llvm.LLVMException("no instr printer for this machine")
|
|
|
|
return self._mip
|
|
|
|
def is_little_endian(self):
|
|
return self.asm_info.isLittleEndian()
|
|
|