[NFC][Py Reformat] Reformat python files in llvm

This is the first commit in a series that will reformat
all the python files in the LLVM repository.

Reformatting is done with `black`.

See more information here:

https://discourse.llvm.org/t/rfc-document-and-standardize-python-code-style

Reviewed By: jhenderson, JDevlieghere, MatzeB

Differential Revision: https://reviews.llvm.org/D150545
This commit is contained in:
Tobias Hieta 2023-05-15 11:02:42 +02:00
parent 7beb2ca8fa
commit b71edfaa4e
No known key found for this signature in database
GPG Key ID: 44F2485E45D59042
262 changed files with 17322 additions and 13455 deletions

View File

@ -1,4 +1,3 @@
from .common import LLVMObject
from .common import c_object_p
from .common import get_library
@ -10,21 +9,25 @@ from ctypes import POINTER
from ctypes import byref
from ctypes import c_char_p
from ctypes import cast
__all__ = ['parse_bitcode']
__all__ = ["parse_bitcode"]
lib = get_library()
def parse_bitcode(mem_buffer):
"""Input is .core.MemoryBuffer"""
module = c_object_p()
result = lib.LLVMParseBitcode2(mem_buffer, byref(module))
if result:
raise RuntimeError('LLVM Error')
raise RuntimeError("LLVM Error")
m = Module(module)
m.take_ownership(mem_buffer)
return m
def register_library(library):
library.LLVMParseBitcode2.argtypes = [MemoryBuffer, POINTER(c_object_p)]
library.LLVMParseBitcode2.restype = bool
register_library(lib)

View File

@ -1,10 +1,10 @@
#===- common.py - Python LLVM Bindings -----------------------*- python -*--===#
# ===- common.py - Python LLVM Bindings -----------------------*- python -*--===#
#
# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
# See https://llvm.org/LICENSE.txt for license information.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#
#===------------------------------------------------------------------------===#
# ===------------------------------------------------------------------------===#
from ctypes import POINTER
from ctypes import c_void_p
@ -15,20 +15,22 @@ import platform
# LLVM_VERSION: sync with PACKAGE_VERSION in CMakeLists.txt
# but leave out the 'svn' suffix.
LLVM_VERSION = '10.0.0'
LLVM_VERSION = "10.0.0"
__all__ = [
'c_object_p',
'get_library',
"c_object_p",
"get_library",
]
c_object_p = POINTER(c_void_p)
class LLVMObject(object):
"""Base class for objects that are backed by an LLVM data structure.
This class should never be instantiated outside of this package.
"""
def __init__(self, ptr, ownable=True, disposer=None):
assert isinstance(ptr, c_object_p)
@ -61,12 +63,13 @@ class LLVMObject(object):
return self._as_parameter_
def __del__(self):
if not hasattr(self, '_self_owned') or not hasattr(self, '_disposer'):
if not hasattr(self, "_self_owned") or not hasattr(self, "_disposer"):
return
if self._self_owned and self._disposer:
self._disposer(self)
class CachedProperty(object):
"""Decorator that caches the result of a property lookup.
@ -74,11 +77,12 @@ class CachedProperty(object):
decorator on properties that invoke C API calls for which the result of the
call will be idempotent.
"""
def __init__(self, wrapped):
self.wrapped = wrapped
try:
self.__doc__ = wrapped.__doc__
except: # pragma: no cover
except: # pragma: no cover
pass
def __get__(self, instance, instance_type=None):
@ -90,6 +94,7 @@ class CachedProperty(object):
return value
def get_library():
"""Obtain a reference to the llvm library."""
@ -101,14 +106,14 @@ def get_library():
# library into a default linker search path. Always Try ctypes.cdll.LoadLibrary()
# with all possible library names first, then try ctypes.util.find_library().
names = ['LLVM-' + LLVM_VERSION, 'LLVM-' + LLVM_VERSION + 'svn']
names = ["LLVM-" + LLVM_VERSION, "LLVM-" + LLVM_VERSION + "svn"]
t = platform.system()
if t == 'Darwin':
pfx, ext = 'lib', '.dylib'
elif t == 'Windows':
pfx, ext = '', '.dll'
if t == "Darwin":
pfx, ext = "lib", ".dylib"
elif t == "Windows":
pfx, ext = "", ".dll"
else:
pfx, ext = 'lib', '.so'
pfx, ext = "lib", ".so"
for i in names:
try:
@ -122,4 +127,4 @@ def get_library():
t = ctypes.util.find_library(i)
if t:
return cdll.LoadLibrary(t)
raise Exception('LLVM shared library not found!')
raise Exception("LLVM shared library not found!")

View File

@ -1,10 +1,10 @@
#===- core.py - Python LLVM Bindings -------------------------*- python -*--===#
# ===- core.py - Python LLVM Bindings -------------------------*- python -*--===#
#
# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
# See https://llvm.org/LICENSE.txt for license information.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#
#===------------------------------------------------------------------------===#
# ===------------------------------------------------------------------------===#
from __future__ import print_function
from .common import LLVMObject
@ -36,6 +36,7 @@ __all__ = [
lib = get_library()
Enums = []
class LLVMEnumeration(object):
"""Represents an individual LLVM enumeration."""
@ -44,8 +45,7 @@ class LLVMEnumeration(object):
self.value = value
def __repr__(self):
return '%s.%s' % (self.__class__.__name__,
self.name)
return "%s.%s" % (self.__class__.__name__, self.name)
@classmethod
def from_value(cls, value):
@ -53,8 +53,7 @@ class LLVMEnumeration(object):
result = cls._value_map.get(value, None)
if result is None:
raise ValueError('Unknown %s: %d' % (cls.__name__,
value))
raise ValueError("Unknown %s: %d" % (cls.__name__, value))
return result
@ -66,12 +65,12 @@ class LLVMEnumeration(object):
enumerations. You should not need to call this outside this module.
"""
if value in cls._value_map:
raise ValueError('%s value already registered: %d' % (cls.__name__,
value))
raise ValueError("%s value already registered: %d" % (cls.__name__, value))
enum = cls(name, value)
cls._value_map[value] = enum
setattr(cls, name, enum)
class Attribute(LLVMEnumeration):
"""Represents an individual Attribute enumeration."""
@ -80,6 +79,7 @@ class Attribute(LLVMEnumeration):
def __init__(self, name, value):
super(Attribute, self).__init__(name, value)
class OpCode(LLVMEnumeration):
"""Represents an individual OpCode enumeration."""
@ -88,6 +88,7 @@ class OpCode(LLVMEnumeration):
def __init__(self, name, value):
super(OpCode, self).__init__(name, value)
class TypeKind(LLVMEnumeration):
"""Represents an individual TypeKind enumeration."""
@ -96,6 +97,7 @@ class TypeKind(LLVMEnumeration):
def __init__(self, name, value):
super(TypeKind, self).__init__(name, value)
class Linkage(LLVMEnumeration):
"""Represents an individual Linkage enumeration."""
@ -104,6 +106,7 @@ class Linkage(LLVMEnumeration):
def __init__(self, name, value):
super(Linkage, self).__init__(name, value)
class Visibility(LLVMEnumeration):
"""Represents an individual visibility enumeration."""
@ -112,6 +115,7 @@ class Visibility(LLVMEnumeration):
def __init__(self, name, value):
super(Visibility, self).__init__(name, value)
class CallConv(LLVMEnumeration):
"""Represents an individual calling convention enumeration."""
@ -120,6 +124,7 @@ class CallConv(LLVMEnumeration):
def __init__(self, name, value):
super(CallConv, self).__init__(name, value)
class IntPredicate(LLVMEnumeration):
"""Represents an individual IntPredicate enumeration."""
@ -128,6 +133,7 @@ class IntPredicate(LLVMEnumeration):
def __init__(self, name, value):
super(IntPredicate, self).__init__(name, value)
class RealPredicate(LLVMEnumeration):
"""Represents an individual RealPredicate enumeration."""
@ -136,6 +142,7 @@ class RealPredicate(LLVMEnumeration):
def __init__(self, name, value):
super(RealPredicate, self).__init__(name, value)
class LandingPadClauseTy(LLVMEnumeration):
"""Represents an individual LandingPadClauseTy enumeration."""
@ -144,6 +151,7 @@ class LandingPadClauseTy(LLVMEnumeration):
def __init__(self, name, value):
super(LandingPadClauseTy, self).__init__(name, value)
class MemoryBuffer(LLVMObject):
"""Represents an opaque memory buffer."""
@ -159,8 +167,9 @@ class MemoryBuffer(LLVMObject):
memory = c_object_p()
out = c_char_p(None)
result = lib.LLVMCreateMemoryBufferWithContentsOfFile(filename,
byref(memory), byref(out))
result = lib.LLVMCreateMemoryBufferWithContentsOfFile(
filename, byref(memory), byref(out)
)
if result:
raise Exception("Could not create memory buffer: %s" % out.value)
@ -170,8 +179,8 @@ class MemoryBuffer(LLVMObject):
def __len__(self):
return lib.LLVMGetBufferSize(self)
class Value(LLVMObject):
def __init__(self, value):
LLVMObject.__init__(self, value)
@ -181,16 +190,17 @@ class Value(LLVMObject):
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."""
@ -232,10 +242,10 @@ class Module(LLVMObject):
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("")
@ -266,25 +276,25 @@ class Module(LLVMObject):
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))
result = lib.LLVMPrintModuleToFile(self, filename, byref(out))
if result:
raise RuntimeError("LLVM Error: %s" % out.value)
class Function(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)
@ -303,10 +313,10 @@ class Function(Value):
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("")
@ -319,18 +329,18 @@ class Function(Value):
if sys.version_info.major == 2:
next = __next__
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)
@ -343,7 +353,7 @@ class BasicBlock(LLVMObject):
def prev(self):
b = lib.LLVMGetPreviousBasicBlock(self)
return b and BasicBlock(b)
@property
def first(self):
i = lib.LLVMGetFirstInstruction(self)
@ -356,7 +366,7 @@ class BasicBlock(LLVMObject):
def __as_value(self):
return Value(lib.LLVMBasicBlockAsValue(self))
@property
def name(self):
return lib.LLVMGetValueName(self.__as_value())
@ -365,28 +375,26 @@ class BasicBlock(LLVMObject):
lib.LLVMDumpValue(self.__as_value())
def get_operand(self, i):
return Value(lib.LLVMGetOperand(self.__as_value(),
i))
return Value(lib.LLVMGetOperand(self.__as_value(), i))
def set_operand(self, i, v):
return lib.LLVMSetOperand(self.__as_value(),
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):
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("")
@ -408,7 +416,6 @@ class BasicBlock(LLVMObject):
class Instruction(Value):
def __init__(self, value):
Value.__init__(self, value)
@ -426,8 +433,8 @@ class Instruction(Value):
def opcode(self):
return OpCode.from_value(lib.LLVMGetInstructionOpcode(self))
class Context(LLVMObject):
class Context(LLVMObject):
def __init__(self, context=None):
if context is None:
context = lib.LLVMContextCreate()
@ -439,6 +446,7 @@ class Context(LLVMObject):
def GetGlobalContext(cls):
return Context(lib.LLVMGetGlobalContext())
def register_library(library):
# Initialization/Shutdown declarations.
library.LLVMShutdown.argtypes = []
@ -455,8 +463,11 @@ def register_library(library):
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.argtypes = [
c_char_p,
POINTER(c_object_p),
POINTER(c_char_p),
]
library.LLVMCreateMemoryBufferWithContentsOfFile.restype = bool
library.LLVMGetBufferSize.argtypes = [MemoryBuffer]
@ -485,8 +496,7 @@ def register_library(library):
library.LLVMDumpModule.argtypes = [Module]
library.LLVMDumpModule.restype = None
library.LLVMPrintModuleToFile.argtypes = [Module, c_char_p,
POINTER(c_char_p)]
library.LLVMPrintModuleToFile.argtypes = [Module, c_char_p, POINTER(c_char_p)]
library.LLVMPrintModuleToFile.restype = bool
library.LLVMGetFirstFunction.argtypes = [Module]
@ -552,6 +562,7 @@ def register_library(library):
library.LLVMGetInstructionOpcode.argtypes = [Instruction]
library.LLVMGetInstructionOpcode.restype = c_uint
def register_enumerations():
if Enums:
return None
@ -572,9 +583,11 @@ def register_enumerations():
enum_class.register(name, value)
return enums
def initialize_llvm():
Context.GetGlobalContext()
register_library(lib)
Enums = register_enumerations()
initialize_llvm()

View File

@ -1,10 +1,10 @@
#===- disassembler.py - Python LLVM Bindings -----------------*- python -*--===#
# ===- disassembler.py - Python LLVM Bindings -----------------*- python -*--===#
#
# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
# See https://llvm.org/LICENSE.txt for license information.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#
#===------------------------------------------------------------------------===#
# ===------------------------------------------------------------------------===#
from ctypes import CFUNCTYPE
from ctypes import POINTER
@ -23,7 +23,7 @@ from .common import c_object_p
from .common import get_library
__all__ = [
'Disassembler',
"Disassembler",
]
lib = get_library()
@ -33,9 +33,23 @@ callbacks = {}
Option_UseMarkup = 1
_initialized = False
_targets = ['AArch64', 'ARM', 'Hexagon', 'MSP430', 'Mips', 'NVPTX', 'PowerPC', 'R600', 'Sparc', 'SystemZ', 'X86', 'XCore']
_targets = [
"AArch64",
"ARM",
"Hexagon",
"MSP430",
"Mips",
"NVPTX",
"PowerPC",
"R600",
"Sparc",
"SystemZ",
"X86",
"XCore",
]
def _ensure_initialized():
global _initialized
if not _initialized:
@ -63,6 +77,7 @@ class Disassembler(LLVMObject):
Disassembler instances can disassemble instructions from multiple sources.
"""
def __init__(self, triple):
"""Create a new disassembler instance.
@ -72,11 +87,15 @@ class Disassembler(LLVMObject):
_ensure_initialized()
ptr = lib.LLVMCreateDisasm(c_char_p(triple), c_void_p(None), c_int(0),
callbacks['op_info'](0), callbacks['symbol_lookup'](0))
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:
raise Exception('Could not obtain disassembler for triple: %s' %
triple)
raise Exception("Could not obtain disassembler for triple: %s" % triple)
LLVMObject.__init__(self, ptr, disposer=lib.LLVMDisasmDispose)
@ -100,8 +119,9 @@ class Disassembler(LLVMObject):
buf = cast(c_char_p(source), POINTER(c_ubyte))
out_str = cast((c_byte * 255)(), c_char_p)
result = lib.LLVMDisasmInstruction(self, buf, c_uint64(len(source)),
c_uint64(pc), out_str, 255)
result = lib.LLVMDisasmInstruction(
self, buf, c_uint64(len(source)), c_uint64(pc), out_str, 255
)
return (result, out_str.value)
@ -128,9 +148,9 @@ class Disassembler(LLVMObject):
end_address = pc + len(source)
while address < end_address:
b = cast(addressof(buf) + offset, POINTER(c_ubyte))
result = lib.LLVMDisasmInstruction(self, b,
c_uint64(len(source) - offset), c_uint64(address),
out_str, 255)
result = lib.LLVMDisasmInstruction(
self, b, c_uint64(len(source) - offset), c_uint64(address), out_str, 255
)
if result == 0:
break
@ -142,28 +162,40 @@ class Disassembler(LLVMObject):
def set_options(self, options):
if not lib.LLVMSetDisasmOptions(self, options):
raise Exception('Unable to set all disassembler options in %i' % options)
raise Exception("Unable to set all disassembler options in %i" % options)
def register_library(library):
library.LLVMCreateDisasm.argtypes = [c_char_p, c_void_p, c_int,
callbacks['op_info'], callbacks['symbol_lookup']]
library.LLVMCreateDisasm.argtypes = [
c_char_p,
c_void_p,
c_int,
callbacks["op_info"],
callbacks["symbol_lookup"],
]
library.LLVMCreateDisasm.restype = c_object_p
library.LLVMDisasmDispose.argtypes = [Disassembler]
library.LLVMDisasmInstruction.argtypes = [Disassembler, POINTER(c_ubyte),
c_uint64, c_uint64, c_char_p, c_size_t]
library.LLVMDisasmInstruction.argtypes = [
Disassembler,
POINTER(c_ubyte),
c_uint64,
c_uint64,
c_char_p,
c_size_t,
]
library.LLVMDisasmInstruction.restype = c_size_t
library.LLVMSetDisasmOptions.argtypes = [Disassembler, c_uint64]
library.LLVMSetDisasmOptions.restype = c_int
callbacks['op_info'] = CFUNCTYPE(c_int, c_void_p, c_uint64, c_uint64, c_uint64,
c_int, c_void_p)
callbacks['symbol_lookup'] = CFUNCTYPE(c_char_p, c_void_p, c_uint64,
POINTER(c_uint64), c_uint64,
POINTER(c_char_p))
callbacks["op_info"] = CFUNCTYPE(
c_int, c_void_p, c_uint64, c_uint64, c_uint64, c_int, c_void_p
)
callbacks["symbol_lookup"] = CFUNCTYPE(
c_char_p, c_void_p, c_uint64, POINTER(c_uint64), c_uint64, POINTER(c_char_p)
)
register_library(lib)

View File

@ -1,10 +1,10 @@
#===- enumerations.py - Python LLVM Enumerations -------------*- python -*--===#
# ===- enumerations.py - Python LLVM Enumerations -------------*- python -*--===#
#
# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
# See https://llvm.org/LICENSE.txt for license information.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#
#===------------------------------------------------------------------------===#
# ===------------------------------------------------------------------------===#
r"""
LLVM Enumerations
@ -18,193 +18,193 @@ defined in this file so they are easier to locate and maintain.
"""
__all__ = [
'Attributes',
'OpCodes',
'TypeKinds',
'Linkages',
'Visibility',
'CallConv',
'IntPredicate',
'RealPredicate',
'LandingPadClauseTy',
"Attributes",
"OpCodes",
"TypeKinds",
"Linkages",
"Visibility",
"CallConv",
"IntPredicate",
"RealPredicate",
"LandingPadClauseTy",
]
Attributes = [
('ZExt', 1 << 0),
('MSExt', 1 << 1),
('NoReturn', 1 << 2),
('InReg', 1 << 3),
('StructRet', 1 << 4),
('NoUnwind', 1 << 5),
('NoAlias', 1 << 6),
('ByVal', 1 << 7),
('Nest', 1 << 8),
('ReadNone', 1 << 9),
('ReadOnly', 1 << 10),
('NoInline', 1 << 11),
('AlwaysInline', 1 << 12),
('OptimizeForSize', 1 << 13),
('StackProtect', 1 << 14),
('StackProtectReq', 1 << 15),
('Alignment', 31 << 16),
('NoCapture', 1 << 21),
('NoRedZone', 1 << 22),
('ImplicitFloat', 1 << 23),
('Naked', 1 << 24),
('InlineHint', 1 << 25),
('StackAlignment', 7 << 26),
('ReturnsTwice', 1 << 29),
('UWTable', 1 << 30),
('NonLazyBind', 1 << 31),
("ZExt", 1 << 0),
("MSExt", 1 << 1),
("NoReturn", 1 << 2),
("InReg", 1 << 3),
("StructRet", 1 << 4),
("NoUnwind", 1 << 5),
("NoAlias", 1 << 6),
("ByVal", 1 << 7),
("Nest", 1 << 8),
("ReadNone", 1 << 9),
("ReadOnly", 1 << 10),
("NoInline", 1 << 11),
("AlwaysInline", 1 << 12),
("OptimizeForSize", 1 << 13),
("StackProtect", 1 << 14),
("StackProtectReq", 1 << 15),
("Alignment", 31 << 16),
("NoCapture", 1 << 21),
("NoRedZone", 1 << 22),
("ImplicitFloat", 1 << 23),
("Naked", 1 << 24),
("InlineHint", 1 << 25),
("StackAlignment", 7 << 26),
("ReturnsTwice", 1 << 29),
("UWTable", 1 << 30),
("NonLazyBind", 1 << 31),
]
OpCodes = [
('Ret', 1),
('Br', 2),
('Switch', 3),
('IndirectBr', 4),
('Invoke', 5),
('Unreachable', 7),
('Add', 8),
('FAdd', 9),
('Sub', 10),
('FSub', 11),
('Mul', 12),
('FMul', 13),
('UDiv', 14),
('SDiv', 15),
('FDiv', 16),
('URem', 17),
('SRem', 18),
('FRem', 19),
('Shl', 20),
('LShr', 21),
('AShr', 22),
('And', 23),
('Or', 24),
('Xor', 25),
('Alloca', 26),
('Load', 27),
('Store', 28),
('GetElementPtr', 29),
('Trunc', 30),
('ZExt', 31),
('SExt', 32),
('FPToUI', 33),
('FPToSI', 34),
('UIToFP', 35),
('SIToFP', 36),
('FPTrunc', 37),
('FPExt', 38),
('PtrToInt', 39),
('IntToPtr', 40),
('BitCast', 41),
('ICmp', 42),
('FCmpl', 43),
('PHI', 44),
('Call', 45),
('Select', 46),
('UserOp1', 47),
('UserOp2', 48),
('AArg', 49),
('ExtractElement', 50),
('InsertElement', 51),
('ShuffleVector', 52),
('ExtractValue', 53),
('InsertValue', 54),
('Fence', 55),
('AtomicCmpXchg', 56),
('AtomicRMW', 57),
('Resume', 58),
('LandingPad', 59),
("Ret", 1),
("Br", 2),
("Switch", 3),
("IndirectBr", 4),
("Invoke", 5),
("Unreachable", 7),
("Add", 8),
("FAdd", 9),
("Sub", 10),
("FSub", 11),
("Mul", 12),
("FMul", 13),
("UDiv", 14),
("SDiv", 15),
("FDiv", 16),
("URem", 17),
("SRem", 18),
("FRem", 19),
("Shl", 20),
("LShr", 21),
("AShr", 22),
("And", 23),
("Or", 24),
("Xor", 25),
("Alloca", 26),
("Load", 27),
("Store", 28),
("GetElementPtr", 29),
("Trunc", 30),
("ZExt", 31),
("SExt", 32),
("FPToUI", 33),
("FPToSI", 34),
("UIToFP", 35),
("SIToFP", 36),
("FPTrunc", 37),
("FPExt", 38),
("PtrToInt", 39),
("IntToPtr", 40),
("BitCast", 41),
("ICmp", 42),
("FCmpl", 43),
("PHI", 44),
("Call", 45),
("Select", 46),
("UserOp1", 47),
("UserOp2", 48),
("AArg", 49),
("ExtractElement", 50),
("InsertElement", 51),
("ShuffleVector", 52),
("ExtractValue", 53),
("InsertValue", 54),
("Fence", 55),
("AtomicCmpXchg", 56),
("AtomicRMW", 57),
("Resume", 58),
("LandingPad", 59),
]
TypeKinds = [
('Void', 0),
('Half', 1),
('Float', 2),
('Double', 3),
('X86_FP80', 4),
('FP128', 5),
('PPC_FP128', 6),
('Label', 7),
('Integer', 8),
('Function', 9),
('Struct', 10),
('Array', 11),
('Pointer', 12),
('Vector', 13),
('Metadata', 14),
('X86_MMX', 15),
("Void", 0),
("Half", 1),
("Float", 2),
("Double", 3),
("X86_FP80", 4),
("FP128", 5),
("PPC_FP128", 6),
("Label", 7),
("Integer", 8),
("Function", 9),
("Struct", 10),
("Array", 11),
("Pointer", 12),
("Vector", 13),
("Metadata", 14),
("X86_MMX", 15),
]
Linkages = [
('External', 0),
('AvailableExternally', 1),
('LinkOnceAny', 2),
('LinkOnceODR', 3),
('WeakAny', 4),
('WeakODR', 5),
('Appending', 6),
('Internal', 7),
('Private', 8),
('DLLImport', 9),
('DLLExport', 10),
('ExternalWeak', 11),
('Ghost', 12),
('Common', 13),
('LinkerPrivate', 14),
('LinkerPrivateWeak', 15),
('LinkerPrivateWeakDefAuto', 16),
("External", 0),
("AvailableExternally", 1),
("LinkOnceAny", 2),
("LinkOnceODR", 3),
("WeakAny", 4),
("WeakODR", 5),
("Appending", 6),
("Internal", 7),
("Private", 8),
("DLLImport", 9),
("DLLExport", 10),
("ExternalWeak", 11),
("Ghost", 12),
("Common", 13),
("LinkerPrivate", 14),
("LinkerPrivateWeak", 15),
("LinkerPrivateWeakDefAuto", 16),
]
Visibility = [
('Default', 0),
('Hidden', 1),
('Protected', 2),
("Default", 0),
("Hidden", 1),
("Protected", 2),
]
CallConv = [
('CCall', 0),
('FastCall', 8),
('ColdCall', 9),
('X86StdcallCall', 64),
('X86FastcallCall', 65),
("CCall", 0),
("FastCall", 8),
("ColdCall", 9),
("X86StdcallCall", 64),
("X86FastcallCall", 65),
]
IntPredicate = [
('EQ', 32),
('NE', 33),
('UGT', 34),
('UGE', 35),
('ULT', 36),
('ULE', 37),
('SGT', 38),
('SGE', 39),
('SLT', 40),
('SLE', 41),
("EQ", 32),
("NE", 33),
("UGT", 34),
("UGE", 35),
("ULT", 36),
("ULE", 37),
("SGT", 38),
("SGE", 39),
("SLT", 40),
("SLE", 41),
]
RealPredicate = [
('PredicateFalse', 0),
('OEQ', 1),
('OGT', 2),
('OGE', 3),
('OLT', 4),
('OLE', 5),
('ONE', 6),
('ORD', 7),
('UNO', 8),
('UEQ', 9),
('UGT', 10),
('UGE', 11),
('ULT', 12),
('ULE', 13),
('UNE', 14),
('PredicateTrue', 15),
("PredicateFalse", 0),
("OEQ", 1),
("OGT", 2),
("OGE", 3),
("OLT", 4),
("OLE", 5),
("ONE", 6),
("ORD", 7),
("UNO", 8),
("UEQ", 9),
("UGT", 10),
("UGE", 11),
("ULT", 12),
("ULE", 13),
("UNE", 14),
("PredicateTrue", 15),
]
LandingPadClauseTy = [
('Catch', 0),
('Filter', 1),
("Catch", 0),
("Filter", 1),
]

View File

@ -1,10 +1,10 @@
#===- object.py - Python Object Bindings --------------------*- python -*--===#
# ===- object.py - Python Object Bindings --------------------*- python -*--===#
#
# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
# See https://llvm.org/LICENSE.txt for license information.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#
#===------------------------------------------------------------------------===#
# ===------------------------------------------------------------------------===#
r"""
Object File Interface
@ -96,6 +96,7 @@ __all__ = [
"Symbol",
]
class ObjectFile(LLVMObject):
"""Represents an object/binary file."""
@ -113,7 +114,7 @@ class ObjectFile(LLVMObject):
contents = MemoryBuffer(filename=filename)
if contents is None:
raise Exception('No input found.')
raise Exception("No input found.")
ptr = lib.LLVMCreateObjectFile(contents)
LLVMObject.__init__(self, ptr, disposer=lib.LLVMDisposeObjectFile)
@ -175,6 +176,7 @@ class ObjectFile(LLVMObject):
lib.LLVMDisposeSymbolIterator(symbols)
class Section(LLVMObject):
"""Represents a section in an object file."""
@ -196,7 +198,7 @@ class Section(LLVMObject):
This is typically something like '.dynsym' or '.rodata'.
"""
if self.expired:
raise Exception('Section instance has expired.')
raise Exception("Section instance has expired.")
return lib.LLVMGetSectionName(self)
@ -204,14 +206,14 @@ class Section(LLVMObject):
def size(self):
"""The size of the section, in long bytes."""
if self.expired:
raise Exception('Section instance has expired.')
raise Exception("Section instance has expired.")
return lib.LLVMGetSectionSize(self)
@CachedProperty
def contents(self):
if self.expired:
raise Exception('Section instance has expired.')
raise Exception("Section instance has expired.")
siz = self.size
@ -224,14 +226,14 @@ class Section(LLVMObject):
def address(self):
"""The address of this section, in long bytes."""
if self.expired:
raise Exception('Section instance has expired.')
raise Exception("Section instance has expired.")
return lib.LLVMGetSectionAddress(self)
def has_symbol(self, symbol):
"""Returns whether a Symbol instance is present in this Section."""
if self.expired:
raise Exception('Section instance has expired.')
raise Exception("Section instance has expired.")
assert isinstance(symbol, Symbol)
return lib.LLVMGetSectionContainsSymbol(self, symbol)
@ -245,7 +247,7 @@ class Section(LLVMObject):
on iterators for more.
"""
if self.expired:
raise Exception('Section instance has expired.')
raise Exception("Section instance has expired.")
relocations = lib.LLVMGetRelocations(self)
last = None
@ -274,10 +276,10 @@ class Section(LLVMObject):
limitation. When called, the properties of the Section are fetched so
they are still available after the Section has been marked inactive.
"""
getattr(self, 'name')
getattr(self, 'size')
getattr(self, 'contents')
getattr(self, 'address')
getattr(self, "name")
getattr(self, "size")
getattr(self, "contents")
getattr(self, "address")
def expire(self):
"""Expire the section.
@ -286,8 +288,10 @@ class Section(LLVMObject):
"""
self.expired = True
class Symbol(LLVMObject):
"""Represents a symbol in an object file."""
def __init__(self, ptr, object_file):
assert isinstance(ptr, c_object_p)
assert isinstance(object_file, ObjectFile)
@ -305,7 +309,7 @@ class Symbol(LLVMObject):
mangling could be in effect.
"""
if self.expired:
raise Exception('Symbol instance has expired.')
raise Exception("Symbol instance has expired.")
return lib.LLVMGetSymbolName(self)
@ -313,7 +317,7 @@ class Symbol(LLVMObject):
def address(self):
"""The address of this symbol, in long bytes."""
if self.expired:
raise Exception('Symbol instance has expired.')
raise Exception("Symbol instance has expired.")
return lib.LLVMGetSymbolAddress(self)
@ -321,7 +325,7 @@ class Symbol(LLVMObject):
def size(self):
"""The size of the symbol, in long bytes."""
if self.expired:
raise Exception('Symbol instance has expired.')
raise Exception("Symbol instance has expired.")
return lib.LLVMGetSymbolSize(self)
@ -342,9 +346,9 @@ class Symbol(LLVMObject):
def cache(self):
"""Cache all cacheable properties."""
getattr(self, 'name')
getattr(self, 'address')
getattr(self, 'size')
getattr(self, "name")
getattr(self, "address")
getattr(self, "size")
def expire(self):
"""Mark the object as expired to prevent future API accesses.
@ -354,8 +358,10 @@ class Symbol(LLVMObject):
"""
self.expired = True
class Relocation(LLVMObject):
"""Represents a relocation definition."""
def __init__(self, ptr):
"""Create a new relocation instance.
@ -374,7 +380,7 @@ class Relocation(LLVMObject):
def offset(self):
"""The offset of this relocation, in long bytes."""
if self.expired:
raise Exception('Relocation instance has expired.')
raise Exception("Relocation instance has expired.")
return lib.LLVMGetRelocationOffset(self)
@ -382,7 +388,7 @@ class Relocation(LLVMObject):
def symbol(self):
"""The Symbol corresponding to this Relocation."""
if self.expired:
raise Exception('Relocation instance has expired.')
raise Exception("Relocation instance has expired.")
ptr = lib.LLVMGetRelocationSymbol(self)
return Symbol(ptr)
@ -391,7 +397,7 @@ class Relocation(LLVMObject):
def type_number(self):
"""The relocation type, as a long."""
if self.expired:
raise Exception('Relocation instance has expired.')
raise Exception("Relocation instance has expired.")
return lib.LLVMGetRelocationType(self)
@ -399,14 +405,14 @@ class Relocation(LLVMObject):
def type_name(self):
"""The relocation type's name, as a str."""
if self.expired:
raise Exception('Relocation instance has expired.')
raise Exception("Relocation instance has expired.")
return lib.LLVMGetRelocationTypeName(self)
@CachedProperty
def value_string(self):
if self.expired:
raise Exception('Relocation instance has expired.')
raise Exception("Relocation instance has expired.")
return lib.LLVMGetRelocationValueString(self)
@ -416,12 +422,13 @@ class Relocation(LLVMObject):
def cache(self):
"""Cache all cacheable properties on this instance."""
getattr(self, 'address')
getattr(self, 'offset')
getattr(self, 'symbol')
getattr(self, 'type')
getattr(self, 'type_name')
getattr(self, 'value_string')
getattr(self, "address")
getattr(self, "offset")
getattr(self, "symbol")
getattr(self, "type")
getattr(self, "type_name")
getattr(self, "value_string")
def register_library(library):
"""Register function prototypes with LLVM library instance."""
@ -504,5 +511,6 @@ def register_library(library):
library.LLVMGetRelocationValueString.argtypes = [c_object_p]
library.LLVMGetRelocationValueString.restype = c_char_p
lib = get_library()
register_library(lib)

View File

@ -4,18 +4,19 @@ import unittest
POSSIBLE_TEST_BINARIES = [
'libreadline.so.5',
'libreadline.so.6',
"libreadline.so.5",
"libreadline.so.6",
]
POSSIBLE_TEST_BINARY_PATHS = [
'/usr/lib/debug',
'/lib',
'/usr/lib',
'/usr/local/lib',
'/lib/i386-linux-gnu',
"/usr/lib/debug",
"/lib",
"/usr/lib",
"/usr/local/lib",
"/lib/i386-linux-gnu",
]
class TestBase(unittest.TestCase):
if sys.version_info.major == 2:
assertRaisesRegex = unittest.TestCase.assertRaisesRegexp
@ -33,7 +34,8 @@ class TestBase(unittest.TestCase):
if os.path.exists(path):
return path
raise Exception('No suitable test binaries available!')
raise Exception("No suitable test binaries available!")
get_test_binary.__test__ = False
def get_test_file(self):

View File

@ -8,8 +8,8 @@ from ..core import Context
from ..core import Module
from ..bit_reader import parse_bitcode
class TestBitReader(TestBase):
class TestBitReader(TestBase):
def test_parse_bitcode(self):
source = self.get_test_bc()
m = parse_bitcode(MemoryBuffer(filename=source))

View File

@ -9,6 +9,7 @@ from ..core import Enums
from ..core import OpCode
from ..bit_reader import parse_bitcode
class TestCore(TestBase):
def test_enumerations(self):
for enum_cls, enum_spec in Enums:
@ -77,8 +78,7 @@ class TestCore(TestBase):
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"]
functions = ["f", "f2", "f3", "f4", "f5", "f6", "g1", "g2", "h1", "h2", "h3"]
# Forward
for f in m:
self.assertEqual(f.name, functions[i])
@ -94,7 +94,7 @@ class TestCore(TestBase):
m = parse_bitcode(MemoryBuffer(filename=self.get_test_bc()))
i = 0
bb_list = ['b1', 'b2', 'end']
bb_list = ["b1", "b2", "end"]
f = m.first
while f.name != "f6":
@ -116,10 +116,12 @@ class TestCore(TestBase):
m = parse_bitcode(MemoryBuffer(filename=self.get_test_bc()))
i = 0
inst_list = [('arg1', OpCode.ExtractValue),
('arg2', OpCode.ExtractValue),
('', OpCode.Call),
('', OpCode.Ret)]
inst_list = [
("arg1", OpCode.ExtractValue),
("arg2", OpCode.ExtractValue),
("", OpCode.Call),
("", OpCode.Ret),
]
bb = m.first.first

View File

@ -4,42 +4,45 @@ from .base import TestBase
from ..disassembler import Disassembler, Option_UseMarkup
class TestDisassembler(TestBase):
def test_instantiate(self):
Disassembler('i686-apple-darwin9')
Disassembler("i686-apple-darwin9")
def test_basic(self):
sequence = '\x67\xe3\x81' # jcxz -127
triple = 'i686-apple-darwin9'
sequence = "\x67\xe3\x81" # jcxz -127
triple = "i686-apple-darwin9"
disassembler = Disassembler(triple)
count, s = disassembler.get_instruction(sequence)
self.assertEqual(count, 3)
self.assertEqual(s, '\tjcxz\t-127')
self.assertEqual(s, "\tjcxz\t-127")
def test_nonexistent_triple(self):
with self.assertRaisesRegex(Exception, "Could not obtain disassembler for triple"):
with self.assertRaisesRegex(
Exception, "Could not obtain disassembler for triple"
):
Disassembler("nonexistent-triple-raises")
def test_get_instructions(self):
sequence = '\x67\xe3\x81\x01\xc7' # jcxz -127; addl %eax, %edi
sequence = "\x67\xe3\x81\x01\xc7" # jcxz -127; addl %eax, %edi
disassembler = Disassembler('i686-apple-darwin9')
disassembler = Disassembler("i686-apple-darwin9")
instructions = list(disassembler.get_instructions(sequence))
self.assertEqual(len(instructions), 2)
self.assertEqual(instructions[0], (0, 3, '\tjcxz\t-127'))
self.assertEqual(instructions[1], (3, 2, '\taddl\t%eax, %edi'))
self.assertEqual(instructions[0], (0, 3, "\tjcxz\t-127"))
self.assertEqual(instructions[1], (3, 2, "\taddl\t%eax, %edi"))
def test_set_options(self):
sequence = '\x10\x40\x2d\xe9'
triple = 'arm-linux-android'
sequence = "\x10\x40\x2d\xe9"
triple = "arm-linux-android"
disassembler = Disassembler(triple)
disassembler.set_options(Option_UseMarkup)
count, s = disassembler.get_instruction(sequence)
print(s)
self.assertEqual(count, 4)
self.assertEqual(s, '\tpush\t{<reg:r4>, <reg:lr>}')
self.assertEqual(s, "\tpush\t{<reg:r4>, <reg:lr>}")

View File

@ -6,6 +6,7 @@ from ..object import Relocation
from ..object import Section
from ..object import Symbol
class TestObjectFile(TestBase):
def get_object_file(self):
source = self.get_test_binary()

View File

@ -17,211 +17,209 @@ from datetime import date
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#sys.path.insert(0, os.path.abspath('.'))
# sys.path.insert(0, os.path.abspath('.'))
# -- General configuration -----------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'
# needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['sphinx.ext.intersphinx', 'sphinx.ext.todo']
extensions = ["sphinx.ext.intersphinx", "sphinx.ext.todo"]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
templates_path = ["_templates"]
# The suffix of source filenames.
source_suffix = {
'.rst': 'restructuredtext',
".rst": "restructuredtext",
}
try:
import recommonmark
import recommonmark
except ImportError:
# manpages do not use any .md sources
if not tags.has('builder-man'):
raise
# manpages do not use any .md sources
if not tags.has("builder-man"):
raise
else:
import sphinx
if sphinx.version_info >= (3, 0):
# This requires 0.5 or later.
extensions.append('recommonmark')
else:
source_parsers = {'.md': 'recommonmark.parser.CommonMarkParser'}
source_suffix['.md'] = 'markdown'
import sphinx
if sphinx.version_info >= (3, 0):
# This requires 0.5 or later.
extensions.append("recommonmark")
else:
source_parsers = {".md": "recommonmark.parser.CommonMarkParser"}
source_suffix[".md"] = "markdown"
# The encoding of source files.
#source_encoding = 'utf-8-sig'
# source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
master_doc = "index"
# General information about the project.
project = u'LLVM'
copyright = u'2003-%d, LLVM Project' % date.today().year
project = "LLVM"
copyright = "2003-%d, LLVM Project" % date.today().year
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None
# language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# today = ''
# Else, today_fmt is used as the format for a strftime call.
today_fmt = '%Y-%m-%d'
today_fmt = "%Y-%m-%d"
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_build']
exclude_patterns = ["_build"]
# The reST default role (used for this markup: `text`) to use for all documents.
#default_role = None
# default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
# add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True
# add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
show_authors = True
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'friendly'
pygments_style = "friendly"
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
# modindex_common_prefix = []
# -- Options for HTML output ---------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'llvm-theme'
html_theme = "llvm-theme"
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
html_theme_options = { "nosidebar": False }
html_theme_options = {"nosidebar": False}
# Add any paths that contain custom themes here, relative to this directory.
html_theme_path = ["_themes"]
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
#html_title = None
# html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
# html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None
# html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
# html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
html_static_path = ["_static"]
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
html_last_updated_fmt = '%Y-%m-%d'
html_last_updated_fmt = "%Y-%m-%d"
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
# html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
html_sidebars = {
'**': [
'indexsidebar.html',
'sourcelink.html',
'searchbox.html',
"**": [
"indexsidebar.html",
"sourcelink.html",
"searchbox.html",
]
}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
# html_additional_pages = {}
# If false, no module index is generated.
#html_domain_indices = True
# html_domain_indices = True
# If false, no index is generated.
#html_use_index = True
# html_use_index = True
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# html_split_index = False
# If true, links to the reST sources are added to the pages.
html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True
# html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True
# html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
# html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None
# html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = 'LLVMdoc'
htmlhelp_basename = "LLVMdoc"
# -- Options for LaTeX output --------------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#'preamble': '',
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#'preamble': '',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('index', 'LLVM.tex', u'LLVM Documentation',
u'LLVM project', 'manual'),
("index", "LLVM.tex", "LLVM Documentation", "LLVM project", "manual"),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None
# latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False
# latex_use_parts = False
# If true, show page references after internal links.
#latex_show_pagerefs = False
# latex_show_pagerefs = False
# If true, show URL addresses after external links.
#latex_show_urls = False
# latex_show_urls = False
# Documents to append as an appendix to all manuals.
#latex_appendices = []
# latex_appendices = []
# If false, no module index is generated.
#latex_domain_indices = True
# latex_domain_indices = True
# -- Options for manual page output --------------------------------------------
@ -234,59 +232,73 @@ man_pages = []
# guide subdirectory.
basedir = os.path.dirname(__file__)
man_page_authors = "Maintained by the LLVM Team (https://llvm.org/)."
command_guide_subpath = 'CommandGuide'
command_guide_subpath = "CommandGuide"
command_guide_path = os.path.join(basedir, command_guide_subpath)
manpages_url = '{page}.html'
manpages_url = "{page}.html"
def process_md(name):
file_subpath = os.path.join(command_guide_subpath, name)
with open(os.path.join(command_guide_path, name)) as f:
title = f.readline().rstrip('\n')
title = f.readline().rstrip("\n")
m = re.match(r'^# (\S+) - (.+)$', title)
m = re.match(r"^# (\S+) - (.+)$", title)
if m is None:
print("error: invalid title in %r "
"(expected '# <name> - <description>')" % file_subpath,
file=sys.stderr)
print(
"error: invalid title in %r "
"(expected '# <name> - <description>')" % file_subpath,
file=sys.stderr,
)
else:
man_pages.append((file_subpath.replace('.md',''), m.group(1),
m.group(2), man_page_authors, 1))
man_pages.append(
(
file_subpath.replace(".md", ""),
m.group(1),
m.group(2),
man_page_authors,
1,
)
)
def process_rst(name):
file_subpath = os.path.join(command_guide_subpath, name)
with open(os.path.join(command_guide_path, name)) as f:
title = f.readline().rstrip('\n')
header = f.readline().rstrip('\n')
title = f.readline().rstrip("\n")
header = f.readline().rstrip("\n")
if len(header) != len(title):
print('error: invalid header in %r (does not match title)' %
file_subpath, file=sys.stderr)
if ' - ' not in title:
print("error: invalid title in %r "
"(expected '<name> - <description>')" % file_subpath,
file=sys.stderr)
print(
"error: invalid header in %r (does not match title)" % file_subpath,
file=sys.stderr,
)
if " - " not in title:
print(
"error: invalid title in %r "
"(expected '<name> - <description>')" % file_subpath,
file=sys.stderr,
)
# Split the name out of the title.
name,description = title.split(' - ', 1)
man_pages.append((file_subpath.replace('.rst',''), name,
description, man_page_authors, 1))
name, description = title.split(" - ", 1)
man_pages.append(
(file_subpath.replace(".rst", ""), name, description, man_page_authors, 1)
)
for name in os.listdir(command_guide_path):
# Process Markdown files
if name.endswith('.md'):
if name.endswith(".md"):
process_md(name)
# Process ReST files apart from the index page.
elif name.endswith('.rst') and name != 'index.rst':
elif name.endswith(".rst") and name != "index.rst":
process_rst(name)
# If true, show URL addresses after external links.
#man_show_urls = False
# man_show_urls = False
# FIXME: Define intersphinx configuration.
intersphinx_mapping = {}
# Pygment lexer are sometimes out of date (when parsing LLVM for example) or
# wrong. Suppress the warning so the build doesn't abort.
suppress_warnings = [ 'misc.highlighting_failure' ]
suppress_warnings = ["misc.highlighting_failure"]

View File

@ -5,36 +5,53 @@ from __future__ import print_function
import sys
import random
class TimingScriptGenerator:
"""Used to generate a bash script which will invoke the toy and time it"""
def __init__(self, scriptname, outputname):
self.timeFile = outputname
self.shfile = open(scriptname, 'w')
self.shfile.write("echo \"\" > %s\n" % self.timeFile)
self.shfile = open(scriptname, "w")
self.shfile.write('echo "" > %s\n' % self.timeFile)
def writeTimingCall(self, filename, numFuncs, funcsCalled, totalCalls):
"""Echo some comments and invoke both versions of toy"""
rootname = filename
if '.' in filename:
rootname = filename[:filename.rfind('.')]
self.shfile.write("echo \"%s: Calls %d of %d functions, %d total\" >> %s\n" % (filename, funcsCalled, numFuncs, totalCalls, self.timeFile))
self.shfile.write("echo \"\" >> %s\n" % self.timeFile)
self.shfile.write("echo \"With MCJIT\" >> %s\n" % self.timeFile)
self.shfile.write("/usr/bin/time -f \"Command %C\\n\\tuser time: %U s\\n\\tsytem time: %S s\\n\\tmax set: %M kb\"")
if "." in filename:
rootname = filename[: filename.rfind(".")]
self.shfile.write(
'echo "%s: Calls %d of %d functions, %d total" >> %s\n'
% (filename, funcsCalled, numFuncs, totalCalls, self.timeFile)
)
self.shfile.write('echo "" >> %s\n' % self.timeFile)
self.shfile.write('echo "With MCJIT" >> %s\n' % self.timeFile)
self.shfile.write(
'/usr/bin/time -f "Command %C\\n\\tuser time: %U s\\n\\tsytem time: %S s\\n\\tmax set: %M kb"'
)
self.shfile.write(" -o %s -a " % self.timeFile)
self.shfile.write("./toy-mcjit < %s > %s-mcjit.out 2> %s-mcjit.err\n" % (filename, rootname, rootname))
self.shfile.write("echo \"\" >> %s\n" % self.timeFile)
self.shfile.write("echo \"With JIT\" >> %s\n" % self.timeFile)
self.shfile.write("/usr/bin/time -f \"Command %C\\n\\tuser time: %U s\\n\\tsytem time: %S s\\n\\tmax set: %M kb\"")
self.shfile.write(
"./toy-mcjit < %s > %s-mcjit.out 2> %s-mcjit.err\n"
% (filename, rootname, rootname)
)
self.shfile.write('echo "" >> %s\n' % self.timeFile)
self.shfile.write('echo "With JIT" >> %s\n' % self.timeFile)
self.shfile.write(
'/usr/bin/time -f "Command %C\\n\\tuser time: %U s\\n\\tsytem time: %S s\\n\\tmax set: %M kb"'
)
self.shfile.write(" -o %s -a " % self.timeFile)
self.shfile.write("./toy-jit < %s > %s-jit.out 2> %s-jit.err\n" % (filename, rootname, rootname))
self.shfile.write("echo \"\" >> %s\n" % self.timeFile)
self.shfile.write("echo \"\" >> %s\n" % self.timeFile)
self.shfile.write(
"./toy-jit < %s > %s-jit.out 2> %s-jit.err\n"
% (filename, rootname, rootname)
)
self.shfile.write('echo "" >> %s\n' % self.timeFile)
self.shfile.write('echo "" >> %s\n' % self.timeFile)
class KScriptGenerator:
"""Used to generate random Kaleidoscope code"""
def __init__(self, filename):
self.kfile = open(filename, 'w')
self.kfile = open(filename, "w")
self.nextFuncNum = 1
self.lastFuncNum = None
self.callWeighting = 0.1
@ -80,20 +97,22 @@ class KScriptGenerator:
self.updateCalledFunctionList(subCallee)
def setCallWeighting(self, weight):
""" Sets the probably of generating a function call"""
"""Sets the probably of generating a function call"""
self.callWeighting = weight
def writeln(self, line):
self.kfile.write(line + '\n')
self.kfile.write(line + "\n")
def writeComment(self, comment):
self.writeln('# ' + comment)
self.writeln("# " + comment)
def writeEmptyLine(self):
self.writeln("")
def writePredefinedFunctions(self):
self.writeComment("Define ':' for sequencing: as a low-precedence operator that ignores operands")
self.writeComment(
"Define ':' for sequencing: as a low-precedence operator that ignores operands"
)
self.writeComment("and just returns the RHS.")
self.writeln("def binary : 1 (x y) y;")
self.writeEmptyLine()
@ -105,16 +124,18 @@ class KScriptGenerator:
self.writeComment("Print the result of a function call")
self.writeln("def printresult(N Result)")
self.writeln(" # 'result('")
self.writeln(" putchard(114) : putchard(101) : putchard(115) : putchard(117) : putchard(108) : putchard(116) : putchard(40) :")
self.writeln(" printd(N) :");
self.writeln(
" putchard(114) : putchard(101) : putchard(115) : putchard(117) : putchard(108) : putchard(116) : putchard(40) :"
)
self.writeln(" printd(N) :")
self.writeln(" # ') = '")
self.writeln(" putchard(41) : putchard(32) : putchard(61) : putchard(32) :")
self.writeln(" printd(Result) :");
self.writeln(" printd(Result) :")
self.writeln(" printlf();")
self.writeEmptyLine()
def writeRandomOperation(self, LValue, LHS, RHS):
shouldCallFunc = (self.lastFuncNum > 2 and random.random() < self.callWeighting)
shouldCallFunc = self.lastFuncNum > 2 and random.random() < self.callWeighting
if shouldCallFunc:
funcToCall = random.randrange(1, self.lastFuncNum - 1)
self.updateFunctionCallMap(self.lastFuncNum, funcToCall)
@ -130,7 +151,10 @@ class KScriptGenerator:
self.writeln(" else if %s < %s then" % (RHS, LHS))
self.writeln(" %s = %s %s %s" % (LValue, LHS, operation, RHS))
self.writeln(" else")
self.writeln(" %s = %s %s %f :" % (LValue, LHS, operation, random.uniform(1, 100)))
self.writeln(
" %s = %s %s %f :"
% (LValue, LHS, operation, random.uniform(1, 100))
)
else:
self.writeln(" %s = %s %s %s :" % (LValue, LHS, operation, RHS))
@ -166,27 +190,43 @@ class KScriptGenerator:
self.writeComment("Call the last function")
arg1 = random.uniform(1, 100)
arg2 = random.uniform(1, 100)
self.writeln("printresult(%d, func%d(%f, %f) )" % (self.lastFuncNum, self.lastFuncNum, arg1, arg2))
self.writeln(
"printresult(%d, func%d(%f, %f) )"
% (self.lastFuncNum, self.lastFuncNum, arg1, arg2)
)
self.writeEmptyLine()
self.updateCalledFunctionList(self.lastFuncNum)
def writeFinalFunctionCounts(self):
self.writeComment("Called %d of %d functions" % (len(self.calledFunctions), self.lastFuncNum))
self.writeComment(
"Called %d of %d functions" % (len(self.calledFunctions), self.lastFuncNum)
)
def generateKScript(filename, numFuncs, elementsPerFunc, funcsBetweenExec, callWeighting, timingScript):
""" Generate a random Kaleidoscope script based on the given parameters """
def generateKScript(
filename, numFuncs, elementsPerFunc, funcsBetweenExec, callWeighting, timingScript
):
"""Generate a random Kaleidoscope script based on the given parameters"""
print("Generating " + filename)
print(" %d functions, %d elements per function, %d functions between execution" %
(numFuncs, elementsPerFunc, funcsBetweenExec))
print(
" %d functions, %d elements per function, %d functions between execution"
% (numFuncs, elementsPerFunc, funcsBetweenExec)
)
print(" Call weighting = %f" % callWeighting)
script = KScriptGenerator(filename)
script.setCallWeighting(callWeighting)
script.writeComment("===========================================================================")
script.writeComment(
"==========================================================================="
)
script.writeComment("Auto-generated script")
script.writeComment(" %d functions, %d elements per function, %d functions between execution"
% (numFuncs, elementsPerFunc, funcsBetweenExec))
script.writeComment(
" %d functions, %d elements per function, %d functions between execution"
% (numFuncs, elementsPerFunc, funcsBetweenExec)
)
script.writeComment(" call weighting = %f" % callWeighting)
script.writeComment("===========================================================================")
script.writeComment(
"==========================================================================="
)
script.writeEmptyLine()
script.writePredefinedFunctions()
funcsSinceLastExec = 0
@ -202,20 +242,49 @@ def generateKScript(filename, numFuncs, elementsPerFunc, funcsBetweenExec, callW
script.writeEmptyLine()
script.writeFinalFunctionCounts()
funcsCalled = len(script.calledFunctions)
print(" Called %d of %d functions, %d total" % (funcsCalled, numFuncs, script.totalCallsExecuted))
timingScript.writeTimingCall(filename, numFuncs, funcsCalled, script.totalCallsExecuted)
print(
" Called %d of %d functions, %d total"
% (funcsCalled, numFuncs, script.totalCallsExecuted)
)
timingScript.writeTimingCall(
filename, numFuncs, funcsCalled, script.totalCallsExecuted
)
# Execution begins here
random.seed()
timingScript = TimingScriptGenerator("time-toy.sh", "timing-data.txt")
dataSets = [(5000, 3, 50, 0.50), (5000, 10, 100, 0.10), (5000, 10, 5, 0.10), (5000, 10, 1, 0.0),
(1000, 3, 10, 0.50), (1000, 10, 100, 0.10), (1000, 10, 5, 0.10), (1000, 10, 1, 0.0),
( 200, 3, 2, 0.50), ( 200, 10, 40, 0.10), ( 200, 10, 2, 0.10), ( 200, 10, 1, 0.0)]
dataSets = [
(5000, 3, 50, 0.50),
(5000, 10, 100, 0.10),
(5000, 10, 5, 0.10),
(5000, 10, 1, 0.0),
(1000, 3, 10, 0.50),
(1000, 10, 100, 0.10),
(1000, 10, 5, 0.10),
(1000, 10, 1, 0.0),
(200, 3, 2, 0.50),
(200, 10, 40, 0.10),
(200, 10, 2, 0.10),
(200, 10, 1, 0.0),
]
# Generate the code
for (numFuncs, elementsPerFunc, funcsBetweenExec, callWeighting) in dataSets:
filename = "test-%d-%d-%d-%d.k" % (numFuncs, elementsPerFunc, funcsBetweenExec, int(callWeighting * 100))
generateKScript(filename, numFuncs, elementsPerFunc, funcsBetweenExec, callWeighting, timingScript)
filename = "test-%d-%d-%d-%d.k" % (
numFuncs,
elementsPerFunc,
funcsBetweenExec,
int(callWeighting * 100),
)
generateKScript(
filename,
numFuncs,
elementsPerFunc,
funcsBetweenExec,
callWeighting,
timingScript,
)
print("All done!")

View File

@ -2,71 +2,105 @@
from __future__ import print_function
class TimingScriptGenerator:
"""Used to generate a bash script which will invoke the toy and time it"""
def __init__(self, scriptname, outputname):
self.shfile = open(scriptname, 'w')
self.shfile = open(scriptname, "w")
self.timeFile = outputname
self.shfile.write("echo \"\" > %s\n" % self.timeFile)
self.shfile.write('echo "" > %s\n' % self.timeFile)
def writeTimingCall(self, irname, callname):
"""Echo some comments and invoke both versions of toy"""
rootname = irname
if '.' in irname:
rootname = irname[:irname.rfind('.')]
self.shfile.write("echo \"%s: Calls %s\" >> %s\n" % (callname, irname, self.timeFile))
self.shfile.write("echo \"\" >> %s\n" % self.timeFile)
self.shfile.write("echo \"With MCJIT\" >> %s\n" % self.timeFile)
self.shfile.write("/usr/bin/time -f \"Command %C\\n\\tuser time: %U s\\n\\tsytem time: %S s\\n\\tmax set: %M kb\"")
if "." in irname:
rootname = irname[: irname.rfind(".")]
self.shfile.write(
'echo "%s: Calls %s" >> %s\n' % (callname, irname, self.timeFile)
)
self.shfile.write('echo "" >> %s\n' % self.timeFile)
self.shfile.write('echo "With MCJIT" >> %s\n' % self.timeFile)
self.shfile.write(
'/usr/bin/time -f "Command %C\\n\\tuser time: %U s\\n\\tsytem time: %S s\\n\\tmax set: %M kb"'
)
self.shfile.write(" -o %s -a " % self.timeFile)
self.shfile.write("./toy-mcjit -use-object-cache -input-IR=%s < %s > %s-mcjit.out 2> %s-mcjit.err\n" % (irname, callname, rootname, rootname))
self.shfile.write("echo \"\" >> %s\n" % self.timeFile)
self.shfile.write("echo \"With MCJIT again\" >> %s\n" % self.timeFile)
self.shfile.write("/usr/bin/time -f \"Command %C\\n\\tuser time: %U s\\n\\tsytem time: %S s\\n\\tmax set: %M kb\"")
self.shfile.write(
"./toy-mcjit -use-object-cache -input-IR=%s < %s > %s-mcjit.out 2> %s-mcjit.err\n"
% (irname, callname, rootname, rootname)
)
self.shfile.write('echo "" >> %s\n' % self.timeFile)
self.shfile.write('echo "With MCJIT again" >> %s\n' % self.timeFile)
self.shfile.write(
'/usr/bin/time -f "Command %C\\n\\tuser time: %U s\\n\\tsytem time: %S s\\n\\tmax set: %M kb"'
)
self.shfile.write(" -o %s -a " % self.timeFile)
self.shfile.write("./toy-mcjit -use-object-cache -input-IR=%s < %s > %s-mcjit.out 2> %s-mcjit.err\n" % (irname, callname, rootname, rootname))
self.shfile.write("echo \"\" >> %s\n" % self.timeFile)
self.shfile.write("echo \"With JIT\" >> %s\n" % self.timeFile)
self.shfile.write("/usr/bin/time -f \"Command %C\\n\\tuser time: %U s\\n\\tsytem time: %S s\\n\\tmax set: %M kb\"")
self.shfile.write(
"./toy-mcjit -use-object-cache -input-IR=%s < %s > %s-mcjit.out 2> %s-mcjit.err\n"
% (irname, callname, rootname, rootname)
)
self.shfile.write('echo "" >> %s\n' % self.timeFile)
self.shfile.write('echo "With JIT" >> %s\n' % self.timeFile)
self.shfile.write(
'/usr/bin/time -f "Command %C\\n\\tuser time: %U s\\n\\tsytem time: %S s\\n\\tmax set: %M kb"'
)
self.shfile.write(" -o %s -a " % self.timeFile)
self.shfile.write("./toy-jit -input-IR=%s < %s > %s-mcjit.out 2> %s-mcjit.err\n" % (irname, callname, rootname, rootname))
self.shfile.write("echo \"\" >> %s\n" % self.timeFile)
self.shfile.write("echo \"\" >> %s\n" % self.timeFile)
self.shfile.write(
"./toy-jit -input-IR=%s < %s > %s-mcjit.out 2> %s-mcjit.err\n"
% (irname, callname, rootname, rootname)
)
self.shfile.write('echo "" >> %s\n' % self.timeFile)
self.shfile.write('echo "" >> %s\n' % self.timeFile)
class LibScriptGenerator:
"""Used to generate a bash script which will convert Kaleidoscope files to IR"""
def __init__(self, filename):
self.shfile = open(filename, 'w')
self.shfile = open(filename, "w")
def writeLibGenCall(self, libname, irname):
self.shfile.write("./toy-ir-gen < %s 2> %s\n" % (libname, irname))
def splitScript(inputname, libGenScript, timingScript):
rootname = inputname[:-2]
libname = rootname + "-lib.k"
irname = rootname + "-lib.ir"
callname = rootname + "-call.k"
infile = open(inputname, "r")
libfile = open(libname, "w")
callfile = open(callname, "w")
print("Splitting %s into %s and %s" % (inputname, callname, libname))
for line in infile:
if not line.startswith("#"):
if line.startswith("print"):
callfile.write(line)
else:
libfile.write(line)
libGenScript.writeLibGenCall(libname, irname)
timingScript.writeTimingCall(irname, callname)
rootname = inputname[:-2]
libname = rootname + "-lib.k"
irname = rootname + "-lib.ir"
callname = rootname + "-call.k"
infile = open(inputname, "r")
libfile = open(libname, "w")
callfile = open(callname, "w")
print("Splitting %s into %s and %s" % (inputname, callname, libname))
for line in infile:
if not line.startswith("#"):
if line.startswith("print"):
callfile.write(line)
else:
libfile.write(line)
libGenScript.writeLibGenCall(libname, irname)
timingScript.writeTimingCall(irname, callname)
# Execution begins here
libGenScript = LibScriptGenerator("make-libs.sh")
timingScript = TimingScriptGenerator("time-lib.sh", "lib-timing.txt")
script_list = ["test-5000-3-50-50.k", "test-5000-10-100-10.k", "test-5000-10-5-10.k", "test-5000-10-1-0.k",
"test-1000-3-10-50.k", "test-1000-10-100-10.k", "test-1000-10-5-10.k", "test-1000-10-1-0.k",
"test-200-3-2-50.k", "test-200-10-40-10.k", "test-200-10-2-10.k", "test-200-10-1-0.k"]
script_list = [
"test-5000-3-50-50.k",
"test-5000-10-100-10.k",
"test-5000-10-5-10.k",
"test-5000-10-1-0.k",
"test-1000-3-10-50.k",
"test-1000-10-100-10.k",
"test-1000-10-5-10.k",
"test-1000-10-1-0.k",
"test-200-3-2-50.k",
"test-200-10-40-10.k",
"test-200-10-2-10.k",
"test-200-10-1-0.k",
]
for script in script_list:
splitScript(script, libGenScript, timingScript)
splitScript(script, libGenScript, timingScript)
print("All done!")

View File

@ -5,41 +5,63 @@ from __future__ import print_function
import sys
import random
class TimingScriptGenerator:
"""Used to generate a bash script which will invoke the toy and time it"""
def __init__(self, scriptname, outputname):
self.timeFile = outputname
self.shfile = open(scriptname, 'w')
self.shfile.write("echo \"\" > %s\n" % self.timeFile)
self.shfile = open(scriptname, "w")
self.shfile.write('echo "" > %s\n' % self.timeFile)
def writeTimingCall(self, filename, numFuncs, funcsCalled, totalCalls):
"""Echo some comments and invoke both versions of toy"""
rootname = filename
if '.' in filename:
rootname = filename[:filename.rfind('.')]
self.shfile.write("echo \"%s: Calls %d of %d functions, %d total\" >> %s\n" % (filename, funcsCalled, numFuncs, totalCalls, self.timeFile))
self.shfile.write("echo \"\" >> %s\n" % self.timeFile)
self.shfile.write("echo \"With MCJIT (original)\" >> %s\n" % self.timeFile)
self.shfile.write("/usr/bin/time -f \"Command %C\\n\\tuser time: %U s\\n\\tsytem time: %S s\\n\\tmax set: %M kb\"")
if "." in filename:
rootname = filename[: filename.rfind(".")]
self.shfile.write(
'echo "%s: Calls %d of %d functions, %d total" >> %s\n'
% (filename, funcsCalled, numFuncs, totalCalls, self.timeFile)
)
self.shfile.write('echo "" >> %s\n' % self.timeFile)
self.shfile.write('echo "With MCJIT (original)" >> %s\n' % self.timeFile)
self.shfile.write(
'/usr/bin/time -f "Command %C\\n\\tuser time: %U s\\n\\tsytem time: %S s\\n\\tmax set: %M kb"'
)
self.shfile.write(" -o %s -a " % self.timeFile)
self.shfile.write("./toy -suppress-prompts -use-mcjit=true -enable-lazy-compilation=false < %s > %s-mcjit.out 2> %s-mcjit.err\n" % (filename, rootname, rootname))
self.shfile.write("echo \"\" >> %s\n" % self.timeFile)
self.shfile.write("echo \"With MCJIT (lazy)\" >> %s\n" % self.timeFile)
self.shfile.write("/usr/bin/time -f \"Command %C\\n\\tuser time: %U s\\n\\tsytem time: %S s\\n\\tmax set: %M kb\"")
self.shfile.write(
"./toy -suppress-prompts -use-mcjit=true -enable-lazy-compilation=false < %s > %s-mcjit.out 2> %s-mcjit.err\n"
% (filename, rootname, rootname)
)
self.shfile.write('echo "" >> %s\n' % self.timeFile)
self.shfile.write('echo "With MCJIT (lazy)" >> %s\n' % self.timeFile)
self.shfile.write(
'/usr/bin/time -f "Command %C\\n\\tuser time: %U s\\n\\tsytem time: %S s\\n\\tmax set: %M kb"'
)
self.shfile.write(" -o %s -a " % self.timeFile)
self.shfile.write("./toy -suppress-prompts -use-mcjit=true -enable-lazy-compilation=true < %s > %s-mcjit-lazy.out 2> %s-mcjit-lazy.err\n" % (filename, rootname, rootname))
self.shfile.write("echo \"\" >> %s\n" % self.timeFile)
self.shfile.write("echo \"With JIT\" >> %s\n" % self.timeFile)
self.shfile.write("/usr/bin/time -f \"Command %C\\n\\tuser time: %U s\\n\\tsytem time: %S s\\n\\tmax set: %M kb\"")
self.shfile.write(
"./toy -suppress-prompts -use-mcjit=true -enable-lazy-compilation=true < %s > %s-mcjit-lazy.out 2> %s-mcjit-lazy.err\n"
% (filename, rootname, rootname)
)
self.shfile.write('echo "" >> %s\n' % self.timeFile)
self.shfile.write('echo "With JIT" >> %s\n' % self.timeFile)
self.shfile.write(
'/usr/bin/time -f "Command %C\\n\\tuser time: %U s\\n\\tsytem time: %S s\\n\\tmax set: %M kb"'
)
self.shfile.write(" -o %s -a " % self.timeFile)
self.shfile.write("./toy -suppress-prompts -use-mcjit=false < %s > %s-jit.out 2> %s-jit.err\n" % (filename, rootname, rootname))
self.shfile.write("echo \"\" >> %s\n" % self.timeFile)
self.shfile.write("echo \"\" >> %s\n" % self.timeFile)
self.shfile.write(
"./toy -suppress-prompts -use-mcjit=false < %s > %s-jit.out 2> %s-jit.err\n"
% (filename, rootname, rootname)
)
self.shfile.write('echo "" >> %s\n' % self.timeFile)
self.shfile.write('echo "" >> %s\n' % self.timeFile)
class KScriptGenerator:
"""Used to generate random Kaleidoscope code"""
def __init__(self, filename):
self.kfile = open(filename, 'w')
self.kfile = open(filename, "w")
self.nextFuncNum = 1
self.lastFuncNum = None
self.callWeighting = 0.1
@ -85,20 +107,22 @@ class KScriptGenerator:
self.updateCalledFunctionList(subCallee)
def setCallWeighting(self, weight):
""" Sets the probably of generating a function call"""
"""Sets the probably of generating a function call"""
self.callWeighting = weight
def writeln(self, line):
self.kfile.write(line + '\n')
self.kfile.write(line + "\n")
def writeComment(self, comment):
self.writeln('# ' + comment)
self.writeln("# " + comment)
def writeEmptyLine(self):
self.writeln("")
def writePredefinedFunctions(self):
self.writeComment("Define ':' for sequencing: as a low-precedence operator that ignores operands")
self.writeComment(
"Define ':' for sequencing: as a low-precedence operator that ignores operands"
)
self.writeComment("and just returns the RHS.")
self.writeln("def binary : 1 (x y) y;")
self.writeEmptyLine()
@ -110,16 +134,18 @@ class KScriptGenerator:
self.writeComment("Print the result of a function call")
self.writeln("def printresult(N Result)")
self.writeln(" # 'result('")
self.writeln(" putchard(114) : putchard(101) : putchard(115) : putchard(117) : putchard(108) : putchard(116) : putchard(40) :")
self.writeln(" printd(N) :");
self.writeln(
" putchard(114) : putchard(101) : putchard(115) : putchard(117) : putchard(108) : putchard(116) : putchard(40) :"
)
self.writeln(" printd(N) :")
self.writeln(" # ') = '")
self.writeln(" putchard(41) : putchard(32) : putchard(61) : putchard(32) :")
self.writeln(" printd(Result) :");
self.writeln(" printd(Result) :")
self.writeln(" printlf();")
self.writeEmptyLine()
def writeRandomOperation(self, LValue, LHS, RHS):
shouldCallFunc = (self.lastFuncNum > 2 and random.random() < self.callWeighting)
shouldCallFunc = self.lastFuncNum > 2 and random.random() < self.callWeighting
if shouldCallFunc:
funcToCall = random.randrange(1, self.lastFuncNum - 1)
self.updateFunctionCallMap(self.lastFuncNum, funcToCall)
@ -135,7 +161,10 @@ class KScriptGenerator:
self.writeln(" else if %s < %s then" % (RHS, LHS))
self.writeln(" %s = %s %s %s" % (LValue, LHS, operation, RHS))
self.writeln(" else")
self.writeln(" %s = %s %s %f :" % (LValue, LHS, operation, random.uniform(1, 100)))
self.writeln(
" %s = %s %s %f :"
% (LValue, LHS, operation, random.uniform(1, 100))
)
else:
self.writeln(" %s = %s %s %s :" % (LValue, LHS, operation, RHS))
@ -171,27 +200,43 @@ class KScriptGenerator:
self.writeComment("Call the last function")
arg1 = random.uniform(1, 100)
arg2 = random.uniform(1, 100)
self.writeln("printresult(%d, func%d(%f, %f) )" % (self.lastFuncNum, self.lastFuncNum, arg1, arg2))
self.writeln(
"printresult(%d, func%d(%f, %f) )"
% (self.lastFuncNum, self.lastFuncNum, arg1, arg2)
)
self.writeEmptyLine()
self.updateCalledFunctionList(self.lastFuncNum)
def writeFinalFunctionCounts(self):
self.writeComment("Called %d of %d functions" % (len(self.calledFunctions), self.lastFuncNum))
self.writeComment(
"Called %d of %d functions" % (len(self.calledFunctions), self.lastFuncNum)
)
def generateKScript(filename, numFuncs, elementsPerFunc, funcsBetweenExec, callWeighting, timingScript):
""" Generate a random Kaleidoscope script based on the given parameters """
def generateKScript(
filename, numFuncs, elementsPerFunc, funcsBetweenExec, callWeighting, timingScript
):
"""Generate a random Kaleidoscope script based on the given parameters"""
print("Generating " + filename)
print(" %d functions, %d elements per function, %d functions between execution" %
(numFuncs, elementsPerFunc, funcsBetweenExec))
print(
" %d functions, %d elements per function, %d functions between execution"
% (numFuncs, elementsPerFunc, funcsBetweenExec)
)
print(" Call weighting = %f" % callWeighting)
script = KScriptGenerator(filename)
script.setCallWeighting(callWeighting)
script.writeComment("===========================================================================")
script.writeComment(
"==========================================================================="
)
script.writeComment("Auto-generated script")
script.writeComment(" %d functions, %d elements per function, %d functions between execution"
% (numFuncs, elementsPerFunc, funcsBetweenExec))
script.writeComment(
" %d functions, %d elements per function, %d functions between execution"
% (numFuncs, elementsPerFunc, funcsBetweenExec)
)
script.writeComment(" call weighting = %f" % callWeighting)
script.writeComment("===========================================================================")
script.writeComment(
"==========================================================================="
)
script.writeEmptyLine()
script.writePredefinedFunctions()
funcsSinceLastExec = 0
@ -207,20 +252,49 @@ def generateKScript(filename, numFuncs, elementsPerFunc, funcsBetweenExec, callW
script.writeEmptyLine()
script.writeFinalFunctionCounts()
funcsCalled = len(script.calledFunctions)
print(" Called %d of %d functions, %d total" % (funcsCalled, numFuncs, script.totalCallsExecuted))
timingScript.writeTimingCall(filename, numFuncs, funcsCalled, script.totalCallsExecuted)
print(
" Called %d of %d functions, %d total"
% (funcsCalled, numFuncs, script.totalCallsExecuted)
)
timingScript.writeTimingCall(
filename, numFuncs, funcsCalled, script.totalCallsExecuted
)
# Execution begins here
random.seed()
timingScript = TimingScriptGenerator("time-toy.sh", "timing-data.txt")
dataSets = [(5000, 3, 50, 0.50), (5000, 10, 100, 0.10), (5000, 10, 5, 0.10), (5000, 10, 1, 0.0),
(1000, 3, 10, 0.50), (1000, 10, 100, 0.10), (1000, 10, 5, 0.10), (1000, 10, 1, 0.0),
( 200, 3, 2, 0.50), ( 200, 10, 40, 0.10), ( 200, 10, 2, 0.10), ( 200, 10, 1, 0.0)]
dataSets = [
(5000, 3, 50, 0.50),
(5000, 10, 100, 0.10),
(5000, 10, 5, 0.10),
(5000, 10, 1, 0.0),
(1000, 3, 10, 0.50),
(1000, 10, 100, 0.10),
(1000, 10, 5, 0.10),
(1000, 10, 1, 0.0),
(200, 3, 2, 0.50),
(200, 10, 40, 0.10),
(200, 10, 2, 0.10),
(200, 10, 1, 0.0),
]
# Generate the code
for (numFuncs, elementsPerFunc, funcsBetweenExec, callWeighting) in dataSets:
filename = "test-%d-%d-%d-%d.k" % (numFuncs, elementsPerFunc, funcsBetweenExec, int(callWeighting * 100))
generateKScript(filename, numFuncs, elementsPerFunc, funcsBetweenExec, callWeighting, timingScript)
filename = "test-%d-%d-%d-%d.k" % (
numFuncs,
elementsPerFunc,
funcsBetweenExec,
int(callWeighting * 100),
)
generateKScript(
filename,
numFuncs,
elementsPerFunc,
funcsBetweenExec,
callWeighting,
timingScript,
)
print("All done!")

View File

@ -2,71 +2,108 @@
from __future__ import print_function
class TimingScriptGenerator:
"""Used to generate a bash script which will invoke the toy and time it"""
def __init__(self, scriptname, outputname):
self.shfile = open(scriptname, 'w')
self.shfile = open(scriptname, "w")
self.timeFile = outputname
self.shfile.write("echo \"\" > %s\n" % self.timeFile)
self.shfile.write('echo "" > %s\n' % self.timeFile)
def writeTimingCall(self, irname, callname):
"""Echo some comments and invoke both versions of toy"""
rootname = irname
if '.' in irname:
rootname = irname[:irname.rfind('.')]
self.shfile.write("echo \"%s: Calls %s\" >> %s\n" % (callname, irname, self.timeFile))
self.shfile.write("echo \"\" >> %s\n" % self.timeFile)
self.shfile.write("echo \"With MCJIT\" >> %s\n" % self.timeFile)
self.shfile.write("/usr/bin/time -f \"Command %C\\n\\tuser time: %U s\\n\\tsytem time: %S s\\n\\tmax set: %M kb\"")
if "." in irname:
rootname = irname[: irname.rfind(".")]
self.shfile.write(
'echo "%s: Calls %s" >> %s\n' % (callname, irname, self.timeFile)
)
self.shfile.write('echo "" >> %s\n' % self.timeFile)
self.shfile.write('echo "With MCJIT" >> %s\n' % self.timeFile)
self.shfile.write(
'/usr/bin/time -f "Command %C\\n\\tuser time: %U s\\n\\tsytem time: %S s\\n\\tmax set: %M kb"'
)
self.shfile.write(" -o %s -a " % self.timeFile)
self.shfile.write("./toy -suppress-prompts -use-mcjit=true -enable-lazy-compilation=true -use-object-cache -input-IR=%s < %s > %s-mcjit.out 2> %s-mcjit.err\n" % (irname, callname, rootname, rootname))
self.shfile.write("echo \"\" >> %s\n" % self.timeFile)
self.shfile.write("echo \"With MCJIT again\" >> %s\n" % self.timeFile)
self.shfile.write("/usr/bin/time -f \"Command %C\\n\\tuser time: %U s\\n\\tsytem time: %S s\\n\\tmax set: %M kb\"")
self.shfile.write(
"./toy -suppress-prompts -use-mcjit=true -enable-lazy-compilation=true -use-object-cache -input-IR=%s < %s > %s-mcjit.out 2> %s-mcjit.err\n"
% (irname, callname, rootname, rootname)
)
self.shfile.write('echo "" >> %s\n' % self.timeFile)
self.shfile.write('echo "With MCJIT again" >> %s\n' % self.timeFile)
self.shfile.write(
'/usr/bin/time -f "Command %C\\n\\tuser time: %U s\\n\\tsytem time: %S s\\n\\tmax set: %M kb"'
)
self.shfile.write(" -o %s -a " % self.timeFile)
self.shfile.write("./toy -suppress-prompts -use-mcjit=true -enable-lazy-compilation=true -use-object-cache -input-IR=%s < %s > %s-mcjit.out 2> %s-mcjit.err\n" % (irname, callname, rootname, rootname))
self.shfile.write("echo \"\" >> %s\n" % self.timeFile)
self.shfile.write("echo \"With JIT\" >> %s\n" % self.timeFile)
self.shfile.write("/usr/bin/time -f \"Command %C\\n\\tuser time: %U s\\n\\tsytem time: %S s\\n\\tmax set: %M kb\"")
self.shfile.write(
"./toy -suppress-prompts -use-mcjit=true -enable-lazy-compilation=true -use-object-cache -input-IR=%s < %s > %s-mcjit.out 2> %s-mcjit.err\n"
% (irname, callname, rootname, rootname)
)
self.shfile.write('echo "" >> %s\n' % self.timeFile)
self.shfile.write('echo "With JIT" >> %s\n' % self.timeFile)
self.shfile.write(
'/usr/bin/time -f "Command %C\\n\\tuser time: %U s\\n\\tsytem time: %S s\\n\\tmax set: %M kb"'
)
self.shfile.write(" -o %s -a " % self.timeFile)
self.shfile.write("./toy -suppress-prompts -use-mcjit=false -input-IR=%s < %s > %s-mcjit.out 2> %s-mcjit.err\n" % (irname, callname, rootname, rootname))
self.shfile.write("echo \"\" >> %s\n" % self.timeFile)
self.shfile.write("echo \"\" >> %s\n" % self.timeFile)
self.shfile.write(
"./toy -suppress-prompts -use-mcjit=false -input-IR=%s < %s > %s-mcjit.out 2> %s-mcjit.err\n"
% (irname, callname, rootname, rootname)
)
self.shfile.write('echo "" >> %s\n' % self.timeFile)
self.shfile.write('echo "" >> %s\n' % self.timeFile)
class LibScriptGenerator:
"""Used to generate a bash script which will invoke the toy and time it"""
def __init__(self, filename):
self.shfile = open(filename, 'w')
self.shfile = open(filename, "w")
def writeLibGenCall(self, libname, irname):
self.shfile.write("./toy -suppress-prompts -use-mcjit=false -dump-modules < %s 2> %s\n" % (libname, irname))
self.shfile.write(
"./toy -suppress-prompts -use-mcjit=false -dump-modules < %s 2> %s\n"
% (libname, irname)
)
def splitScript(inputname, libGenScript, timingScript):
rootname = inputname[:-2]
libname = rootname + "-lib.k"
irname = rootname + "-lib.ir"
callname = rootname + "-call.k"
infile = open(inputname, "r")
libfile = open(libname, "w")
callfile = open(callname, "w")
print("Splitting %s into %s and %s" % (inputname, callname, libname))
for line in infile:
if not line.startswith("#"):
if line.startswith("print"):
callfile.write(line)
else:
libfile.write(line)
libGenScript.writeLibGenCall(libname, irname)
timingScript.writeTimingCall(irname, callname)
rootname = inputname[:-2]
libname = rootname + "-lib.k"
irname = rootname + "-lib.ir"
callname = rootname + "-call.k"
infile = open(inputname, "r")
libfile = open(libname, "w")
callfile = open(callname, "w")
print("Splitting %s into %s and %s" % (inputname, callname, libname))
for line in infile:
if not line.startswith("#"):
if line.startswith("print"):
callfile.write(line)
else:
libfile.write(line)
libGenScript.writeLibGenCall(libname, irname)
timingScript.writeTimingCall(irname, callname)
# Execution begins here
libGenScript = LibScriptGenerator("make-libs.sh")
timingScript = TimingScriptGenerator("time-lib.sh", "lib-timing.txt")
script_list = ["test-5000-3-50-50.k", "test-5000-10-100-10.k", "test-5000-10-5-10.k", "test-5000-10-1-0.k",
"test-1000-3-10-50.k", "test-1000-10-100-10.k", "test-1000-10-5-10.k", "test-1000-10-1-0.k",
"test-200-3-2-50.k", "test-200-10-40-10.k", "test-200-10-2-10.k", "test-200-10-1-0.k"]
script_list = [
"test-5000-3-50-50.k",
"test-5000-10-100-10.k",
"test-5000-10-5-10.k",
"test-5000-10-1-0.k",
"test-1000-3-10-50.k",
"test-1000-10-100-10.k",
"test-1000-10-5-10.k",
"test-1000-10-1-0.k",
"test-200-3-2-50.k",
"test-200-10-40-10.k",
"test-200-10-2-10.k",
"test-200-10-1-0.k",
]
for script in script_list:
splitScript(script, libGenScript, timingScript)
splitScript(script, libGenScript, timingScript)
print("All done!")

View File

@ -5,36 +5,53 @@ from __future__ import print_function
import sys
import random
class TimingScriptGenerator:
"""Used to generate a bash script which will invoke the toy and time it"""
def __init__(self, scriptname, outputname):
self.timeFile = outputname
self.shfile = open(scriptname, 'w')
self.shfile.write("echo \"\" > %s\n" % self.timeFile)
self.shfile = open(scriptname, "w")
self.shfile.write('echo "" > %s\n' % self.timeFile)
def writeTimingCall(self, filename, numFuncs, funcsCalled, totalCalls):
"""Echo some comments and invoke both versions of toy"""
rootname = filename
if '.' in filename:
rootname = filename[:filename.rfind('.')]
self.shfile.write("echo \"%s: Calls %d of %d functions, %d total\" >> %s\n" % (filename, funcsCalled, numFuncs, totalCalls, self.timeFile))
self.shfile.write("echo \"\" >> %s\n" % self.timeFile)
self.shfile.write("echo \"With MCJIT\" >> %s\n" % self.timeFile)
self.shfile.write("/usr/bin/time -f \"Command %C\\n\\tuser time: %U s\\n\\tsytem time: %S s\\n\\tmax set: %M kb\"")
if "." in filename:
rootname = filename[: filename.rfind(".")]
self.shfile.write(
'echo "%s: Calls %d of %d functions, %d total" >> %s\n'
% (filename, funcsCalled, numFuncs, totalCalls, self.timeFile)
)
self.shfile.write('echo "" >> %s\n' % self.timeFile)
self.shfile.write('echo "With MCJIT" >> %s\n' % self.timeFile)
self.shfile.write(
'/usr/bin/time -f "Command %C\\n\\tuser time: %U s\\n\\tsytem time: %S s\\n\\tmax set: %M kb"'
)
self.shfile.write(" -o %s -a " % self.timeFile)
self.shfile.write("./toy-mcjit < %s > %s-mcjit.out 2> %s-mcjit.err\n" % (filename, rootname, rootname))
self.shfile.write("echo \"\" >> %s\n" % self.timeFile)
self.shfile.write("echo \"With JIT\" >> %s\n" % self.timeFile)
self.shfile.write("/usr/bin/time -f \"Command %C\\n\\tuser time: %U s\\n\\tsytem time: %S s\\n\\tmax set: %M kb\"")
self.shfile.write(
"./toy-mcjit < %s > %s-mcjit.out 2> %s-mcjit.err\n"
% (filename, rootname, rootname)
)
self.shfile.write('echo "" >> %s\n' % self.timeFile)
self.shfile.write('echo "With JIT" >> %s\n' % self.timeFile)
self.shfile.write(
'/usr/bin/time -f "Command %C\\n\\tuser time: %U s\\n\\tsytem time: %S s\\n\\tmax set: %M kb"'
)
self.shfile.write(" -o %s -a " % self.timeFile)
self.shfile.write("./toy-jit < %s > %s-jit.out 2> %s-jit.err\n" % (filename, rootname, rootname))
self.shfile.write("echo \"\" >> %s\n" % self.timeFile)
self.shfile.write("echo \"\" >> %s\n" % self.timeFile)
self.shfile.write(
"./toy-jit < %s > %s-jit.out 2> %s-jit.err\n"
% (filename, rootname, rootname)
)
self.shfile.write('echo "" >> %s\n' % self.timeFile)
self.shfile.write('echo "" >> %s\n' % self.timeFile)
class KScriptGenerator:
"""Used to generate random Kaleidoscope code"""
def __init__(self, filename):
self.kfile = open(filename, 'w')
self.kfile = open(filename, "w")
self.nextFuncNum = 1
self.lastFuncNum = None
self.callWeighting = 0.1
@ -80,20 +97,22 @@ class KScriptGenerator:
self.updateCalledFunctionList(subCallee)
def setCallWeighting(self, weight):
""" Sets the probably of generating a function call"""
"""Sets the probably of generating a function call"""
self.callWeighting = weight
def writeln(self, line):
self.kfile.write(line + '\n')
self.kfile.write(line + "\n")
def writeComment(self, comment):
self.writeln('# ' + comment)
self.writeln("# " + comment)
def writeEmptyLine(self):
self.writeln("")
def writePredefinedFunctions(self):
self.writeComment("Define ':' for sequencing: as a low-precedence operator that ignores operands")
self.writeComment(
"Define ':' for sequencing: as a low-precedence operator that ignores operands"
)
self.writeComment("and just returns the RHS.")
self.writeln("def binary : 1 (x y) y;")
self.writeEmptyLine()
@ -105,16 +124,18 @@ class KScriptGenerator:
self.writeComment("Print the result of a function call")
self.writeln("def printresult(N Result)")
self.writeln(" # 'result('")
self.writeln(" putchard(114) : putchard(101) : putchard(115) : putchard(117) : putchard(108) : putchard(116) : putchard(40) :")
self.writeln(" printd(N) :");
self.writeln(
" putchard(114) : putchard(101) : putchard(115) : putchard(117) : putchard(108) : putchard(116) : putchard(40) :"
)
self.writeln(" printd(N) :")
self.writeln(" # ') = '")
self.writeln(" putchard(41) : putchard(32) : putchard(61) : putchard(32) :")
self.writeln(" printd(Result) :");
self.writeln(" printd(Result) :")
self.writeln(" printlf();")
self.writeEmptyLine()
def writeRandomOperation(self, LValue, LHS, RHS):
shouldCallFunc = (self.lastFuncNum > 2 and random.random() < self.callWeighting)
shouldCallFunc = self.lastFuncNum > 2 and random.random() < self.callWeighting
if shouldCallFunc:
funcToCall = random.randrange(1, self.lastFuncNum - 1)
self.updateFunctionCallMap(self.lastFuncNum, funcToCall)
@ -130,7 +151,10 @@ class KScriptGenerator:
self.writeln(" else if %s < %s then" % (RHS, LHS))
self.writeln(" %s = %s %s %s" % (LValue, LHS, operation, RHS))
self.writeln(" else")
self.writeln(" %s = %s %s %f :" % (LValue, LHS, operation, random.uniform(1, 100)))
self.writeln(
" %s = %s %s %f :"
% (LValue, LHS, operation, random.uniform(1, 100))
)
else:
self.writeln(" %s = %s %s %s :" % (LValue, LHS, operation, RHS))
@ -166,27 +190,43 @@ class KScriptGenerator:
self.writeComment("Call the last function")
arg1 = random.uniform(1, 100)
arg2 = random.uniform(1, 100)
self.writeln("printresult(%d, func%d(%f, %f) )" % (self.lastFuncNum, self.lastFuncNum, arg1, arg2))
self.writeln(
"printresult(%d, func%d(%f, %f) )"
% (self.lastFuncNum, self.lastFuncNum, arg1, arg2)
)
self.writeEmptyLine()
self.updateCalledFunctionList(self.lastFuncNum)
def writeFinalFunctionCounts(self):
self.writeComment("Called %d of %d functions" % (len(self.calledFunctions), self.lastFuncNum))
self.writeComment(
"Called %d of %d functions" % (len(self.calledFunctions), self.lastFuncNum)
)
def generateKScript(filename, numFuncs, elementsPerFunc, funcsBetweenExec, callWeighting, timingScript):
""" Generate a random Kaleidoscope script based on the given parameters """
def generateKScript(
filename, numFuncs, elementsPerFunc, funcsBetweenExec, callWeighting, timingScript
):
"""Generate a random Kaleidoscope script based on the given parameters"""
print("Generating " + filename)
print(" %d functions, %d elements per function, %d functions between execution" %
(numFuncs, elementsPerFunc, funcsBetweenExec))
print(
" %d functions, %d elements per function, %d functions between execution"
% (numFuncs, elementsPerFunc, funcsBetweenExec)
)
print(" Call weighting = %f" % callWeighting)
script = KScriptGenerator(filename)
script.setCallWeighting(callWeighting)
script.writeComment("===========================================================================")
script.writeComment(
"==========================================================================="
)
script.writeComment("Auto-generated script")
script.writeComment(" %d functions, %d elements per function, %d functions between execution"
% (numFuncs, elementsPerFunc, funcsBetweenExec))
script.writeComment(
" %d functions, %d elements per function, %d functions between execution"
% (numFuncs, elementsPerFunc, funcsBetweenExec)
)
script.writeComment(" call weighting = %f" % callWeighting)
script.writeComment("===========================================================================")
script.writeComment(
"==========================================================================="
)
script.writeEmptyLine()
script.writePredefinedFunctions()
funcsSinceLastExec = 0
@ -202,20 +242,49 @@ def generateKScript(filename, numFuncs, elementsPerFunc, funcsBetweenExec, callW
script.writeEmptyLine()
script.writeFinalFunctionCounts()
funcsCalled = len(script.calledFunctions)
print(" Called %d of %d functions, %d total" % (funcsCalled, numFuncs, script.totalCallsExecuted))
timingScript.writeTimingCall(filename, numFuncs, funcsCalled, script.totalCallsExecuted)
print(
" Called %d of %d functions, %d total"
% (funcsCalled, numFuncs, script.totalCallsExecuted)
)
timingScript.writeTimingCall(
filename, numFuncs, funcsCalled, script.totalCallsExecuted
)
# Execution begins here
random.seed()
timingScript = TimingScriptGenerator("time-toy.sh", "timing-data.txt")
dataSets = [(5000, 3, 50, 0.50), (5000, 10, 100, 0.10), (5000, 10, 5, 0.10), (5000, 10, 1, 0.0),
(1000, 3, 10, 0.50), (1000, 10, 100, 0.10), (1000, 10, 5, 0.10), (1000, 10, 1, 0.0),
( 200, 3, 2, 0.50), ( 200, 10, 40, 0.10), ( 200, 10, 2, 0.10), ( 200, 10, 1, 0.0)]
dataSets = [
(5000, 3, 50, 0.50),
(5000, 10, 100, 0.10),
(5000, 10, 5, 0.10),
(5000, 10, 1, 0.0),
(1000, 3, 10, 0.50),
(1000, 10, 100, 0.10),
(1000, 10, 5, 0.10),
(1000, 10, 1, 0.0),
(200, 3, 2, 0.50),
(200, 10, 40, 0.10),
(200, 10, 2, 0.10),
(200, 10, 1, 0.0),
]
# Generate the code
for (numFuncs, elementsPerFunc, funcsBetweenExec, callWeighting) in dataSets:
filename = "test-%d-%d-%d-%d.k" % (numFuncs, elementsPerFunc, funcsBetweenExec, int(callWeighting * 100))
generateKScript(filename, numFuncs, elementsPerFunc, funcsBetweenExec, callWeighting, timingScript)
filename = "test-%d-%d-%d-%d.k" % (
numFuncs,
elementsPerFunc,
funcsBetweenExec,
int(callWeighting * 100),
)
generateKScript(
filename,
numFuncs,
elementsPerFunc,
funcsBetweenExec,
callWeighting,
timingScript,
)
print("All done!")

View File

@ -11,7 +11,7 @@ import sys
import tensorflow as tf
POLICY_DECISION_LABEL = 'inlining_decision'
POLICY_DECISION_LABEL = "inlining_decision"
POLICY_OUTPUT_SPEC = """
[
{
@ -31,106 +31,110 @@ POLICY_OUTPUT_SPEC = """
# pylint: disable=g-complex-comprehension
def get_input_signature():
"""Returns the list of features for LLVM inlining."""
# int64 features
inputs = [
tf.TensorSpec(dtype=tf.int64, shape=(), name=key) for key in [
'caller_basic_block_count',
'caller_conditionally_executed_blocks',
'caller_users',
'callee_basic_block_count',
'callee_conditionally_executed_blocks',
'callee_users',
'nr_ctant_params',
'node_count',
'edge_count',
'callsite_height',
'cost_estimate',
'inlining_default',
'sroa_savings',
'sroa_losses',
'load_elimination',
'call_penalty',
'call_argument_setup',
'load_relative_intrinsic',
'lowered_call_arg_setup',
'indirect_call_penalty',
'jump_table_penalty',
'case_cluster_penalty',
'switch_penalty',
'unsimplified_common_instructions',
'num_loops',
'dead_blocks',
'simplified_instructions',
'constant_args',
'constant_offset_ptr_args',
'callsite_cost',
'cold_cc_penalty',
'last_call_to_static_bonus',
'is_multiple_blocks',
'nested_inlines',
'nested_inline_cost_estimate',
'threshold',
]
]
"""Returns the list of features for LLVM inlining."""
# int64 features
inputs = [
tf.TensorSpec(dtype=tf.int64, shape=(), name=key)
for key in [
"caller_basic_block_count",
"caller_conditionally_executed_blocks",
"caller_users",
"callee_basic_block_count",
"callee_conditionally_executed_blocks",
"callee_users",
"nr_ctant_params",
"node_count",
"edge_count",
"callsite_height",
"cost_estimate",
"inlining_default",
"sroa_savings",
"sroa_losses",
"load_elimination",
"call_penalty",
"call_argument_setup",
"load_relative_intrinsic",
"lowered_call_arg_setup",
"indirect_call_penalty",
"jump_table_penalty",
"case_cluster_penalty",
"switch_penalty",
"unsimplified_common_instructions",
"num_loops",
"dead_blocks",
"simplified_instructions",
"constant_args",
"constant_offset_ptr_args",
"callsite_cost",
"cold_cc_penalty",
"last_call_to_static_bonus",
"is_multiple_blocks",
"nested_inlines",
"nested_inline_cost_estimate",
"threshold",
]
]
# float32 features
inputs.extend([
tf.TensorSpec(dtype=tf.float32, shape=(), name=key)
for key in ['discount', 'reward']
])
# float32 features
inputs.extend(
[
tf.TensorSpec(dtype=tf.float32, shape=(), name=key)
for key in ["discount", "reward"]
]
)
# int32 features
inputs.extend([
tf.TensorSpec(dtype=tf.int32, shape=(), name=key)
for key in ['step_type']
])
return inputs
# int32 features
inputs.extend(
[tf.TensorSpec(dtype=tf.int32, shape=(), name=key) for key in ["step_type"]]
)
return inputs
def get_output_signature():
return POLICY_DECISION_LABEL
return POLICY_DECISION_LABEL
def get_output_spec():
return POLICY_OUTPUT_SPEC
return POLICY_OUTPUT_SPEC
def get_output_spec_path(path):
return os.path.join(path, 'output_spec.json')
return os.path.join(path, "output_spec.json")
def build_mock_model(path, signature):
"""Build and save the mock model with the given signature"""
module = tf.Module()
def action(*inputs):
return {signature['output']: tf.constant(value=1, dtype=tf.int64)}
"""Build and save the mock model with the given signature"""
module = tf.Module()
module.action = tf.function()(action)
action = {'action': module.action.get_concrete_function(signature['inputs'])}
tf.saved_model.save(module, path, signatures=action)
def action(*inputs):
return {signature["output"]: tf.constant(value=1, dtype=tf.int64)}
output_spec_path = get_output_spec_path(path)
with open(output_spec_path, 'w') as f:
print(f'Writing output spec to {output_spec_path}.')
f.write(signature['output_spec'])
module.action = tf.function()(action)
action = {"action": module.action.get_concrete_function(signature["inputs"])}
tf.saved_model.save(module, path, signatures=action)
output_spec_path = get_output_spec_path(path)
with open(output_spec_path, "w") as f:
print(f"Writing output spec to {output_spec_path}.")
f.write(signature["output_spec"])
def get_signature():
return {
'inputs': get_input_signature(),
'output': get_output_signature(),
'output_spec': get_output_spec()
}
return {
"inputs": get_input_signature(),
"output": get_output_signature(),
"output_spec": get_output_spec(),
}
def main(argv):
assert len(argv) == 2
model_path = argv[1]
assert len(argv) == 2
model_path = argv[1]
print(f'Output model to: [{argv[1]}]')
signature = get_signature()
build_mock_model(model_path, signature)
print(f"Output model to: [{argv[1]}]")
signature = get_signature()
build_mock_model(model_path, signature)
if __name__ == '__main__':
main(sys.argv)
if __name__ == "__main__":
main(sys.argv)

View File

@ -6,7 +6,8 @@ output the first liverange that can be evicted.
import os
import sys
import tensorflow as tf
POLICY_DECISION_LABEL = 'index_to_evict'
POLICY_DECISION_LABEL = "index_to_evict"
POLICY_OUTPUT_SPEC = """
[
{
@ -22,49 +23,50 @@ POLICY_OUTPUT_SPEC = """
}
]
"""
PER_REGISTER_FEATURE_LIST = ['mask']
PER_REGISTER_FEATURE_LIST = ["mask"]
NUM_REGISTERS = 33
def get_input_signature():
"""Returns (time_step_spec, action_spec) for LLVM register allocation."""
inputs = dict(
(key, tf.TensorSpec(dtype=tf.int64, shape=(NUM_REGISTERS), name=key))
for key in PER_REGISTER_FEATURE_LIST)
return inputs
"""Returns (time_step_spec, action_spec) for LLVM register allocation."""
inputs = dict(
(key, tf.TensorSpec(dtype=tf.int64, shape=(NUM_REGISTERS), name=key))
for key in PER_REGISTER_FEATURE_LIST
)
return inputs
def get_output_spec_path(path):
return os.path.join(path, 'output_spec.json')
return os.path.join(path, "output_spec.json")
def build_mock_model(path):
"""Build and save the mock model with the given signature."""
module = tf.Module()
# We have to set this useless variable in order for the TF C API to correctly
# intake it
module.var = tf.Variable(0, dtype=tf.int64)
"""Build and save the mock model with the given signature."""
module = tf.Module()
# We have to set this useless variable in order for the TF C API to correctly
# intake it
module.var = tf.Variable(0, dtype=tf.int64)
def action(*inputs):
result = tf.math.argmax(
tf.cast(inputs[0]['mask'], tf.int32), axis=-1) + module.var
return {POLICY_DECISION_LABEL: result}
module.action = tf.function()(action)
action = {
'action': module.action.get_concrete_function(get_input_signature())
}
tf.saved_model.save(module, path, signatures=action)
output_spec_path = get_output_spec_path(path)
with open(output_spec_path, 'w') as f:
print(f'Writing output spec to {output_spec_path}.')
f.write(POLICY_OUTPUT_SPEC)
def action(*inputs):
result = (
tf.math.argmax(tf.cast(inputs[0]["mask"], tf.int32), axis=-1) + module.var
)
return {POLICY_DECISION_LABEL: result}
module.action = tf.function()(action)
action = {"action": module.action.get_concrete_function(get_input_signature())}
tf.saved_model.save(module, path, signatures=action)
output_spec_path = get_output_spec_path(path)
with open(output_spec_path, "w") as f:
print(f"Writing output spec to {output_spec_path}.")
f.write(POLICY_OUTPUT_SPEC)
def main(argv):
assert len(argv) == 2
model_path = argv[1]
build_mock_model(model_path)
assert len(argv) == 2
model_path = argv[1]
build_mock_model(model_path)
if __name__ == '__main__':
main(sys.argv)
if __name__ == "__main__":
main(sys.argv)

View File

@ -7,7 +7,8 @@ correct input and output parameters.
import os
import sys
import tensorflow as tf
POLICY_DECISION_LABEL = 'priority'
POLICY_DECISION_LABEL = "priority"
POLICY_OUTPUT_SPEC = """
[
{
@ -23,73 +24,83 @@ POLICY_OUTPUT_SPEC = """
}
]
"""
PER_LIVEINTERVAL_INT64_FEATURE_LIST = [
'li_size', 'stage'
]
PER_LIVEINTERVAL_FLOAT32_FEATURE_LIST = ['weight'
]
PER_LIVEINTERVAL_FEATURE_LIST = PER_LIVEINTERVAL_FLOAT32_FEATURE_LIST + \
PER_LIVEINTERVAL_INT64_FEATURE_LIST
CONTEXT_FEATURE_LIST = ('discount', 'reward', 'step_type')
PER_LIVEINTERVAL_INT64_FEATURE_LIST = ["li_size", "stage"]
PER_LIVEINTERVAL_FLOAT32_FEATURE_LIST = ["weight"]
PER_LIVEINTERVAL_FEATURE_LIST = (
PER_LIVEINTERVAL_FLOAT32_FEATURE_LIST + PER_LIVEINTERVAL_INT64_FEATURE_LIST
)
CONTEXT_FEATURE_LIST = ("discount", "reward", "step_type")
def get_input_signature():
"""Returns (time_step_spec, action_spec) for LLVM register allocation."""
inputs = dict(
(key, tf.TensorSpec(dtype=tf.int64, shape=(), name=key))
for key in PER_LIVEINTERVAL_INT64_FEATURE_LIST)
inputs.update(
dict((key,
tf.TensorSpec(dtype=tf.float32, shape=(), name=key))
for key in PER_LIVEINTERVAL_FLOAT32_FEATURE_LIST))
inputs.update(
dict((key, tf.TensorSpec(dtype=tf.float32, shape=(), name=key))
for key in ['discount', 'reward']))
inputs.update(
dict((key, tf.TensorSpec(dtype=tf.int32, shape=(), name=key))
for key in ['step_type']))
return inputs
"""Returns (time_step_spec, action_spec) for LLVM register allocation."""
inputs = dict(
(key, tf.TensorSpec(dtype=tf.int64, shape=(), name=key))
for key in PER_LIVEINTERVAL_INT64_FEATURE_LIST
)
inputs.update(
dict(
(key, tf.TensorSpec(dtype=tf.float32, shape=(), name=key))
for key in PER_LIVEINTERVAL_FLOAT32_FEATURE_LIST
)
)
inputs.update(
dict(
(key, tf.TensorSpec(dtype=tf.float32, shape=(), name=key))
for key in ["discount", "reward"]
)
)
inputs.update(
dict(
(key, tf.TensorSpec(dtype=tf.int32, shape=(), name=key))
for key in ["step_type"]
)
)
return inputs
def get_output_spec_path(path):
return os.path.join(path, 'output_spec.json')
return os.path.join(path, "output_spec.json")
def build_mock_model(path):
"""Build and save the mock model with the given signature."""
module = tf.Module()
# We have to set this useless variable in order for the TF C API to correctly
# intake it
module.var = tf.Variable(0, dtype=tf.float32)
"""Build and save the mock model with the given signature."""
module = tf.Module()
# We have to set this useless variable in order for the TF C API to correctly
# intake it
module.var = tf.Variable(0, dtype=tf.float32)
def action(*inputs):
s1 = tf.reduce_sum([
tf.cast(inputs[0][key], tf.float32) for key in PER_LIVEINTERVAL_FEATURE_LIST
],
axis=0)
s2 = tf.reduce_sum(
[tf.cast(inputs[0][key], tf.float32) for key in CONTEXT_FEATURE_LIST])
# Add a large number so s won't be 0.
s = s1 + s2
result = s + module.var
return {POLICY_DECISION_LABEL: result}
module.action = tf.function()(action)
action = {
'action': module.action.get_concrete_function(get_input_signature())
}
def action(*inputs):
s1 = tf.reduce_sum(
[
tf.cast(inputs[0][key], tf.float32)
for key in PER_LIVEINTERVAL_FEATURE_LIST
],
axis=0,
)
s2 = tf.reduce_sum(
[tf.cast(inputs[0][key], tf.float32) for key in CONTEXT_FEATURE_LIST]
)
# Add a large number so s won't be 0.
s = s1 + s2
result = s + module.var
return {POLICY_DECISION_LABEL: result}
tf.saved_model.save(module, path, signatures=action)
output_spec_path = get_output_spec_path(path)
with open(output_spec_path, 'w') as f:
print(f'Writing output spec to {output_spec_path}.')
f.write(POLICY_OUTPUT_SPEC)
module.action = tf.function()(action)
action = {"action": module.action.get_concrete_function(get_input_signature())}
tf.saved_model.save(module, path, signatures=action)
output_spec_path = get_output_spec_path(path)
with open(output_spec_path, "w") as f:
print(f"Writing output spec to {output_spec_path}.")
f.write(POLICY_OUTPUT_SPEC)
def main(argv):
assert len(argv) == 2
model_path = argv[1]
build_mock_model(model_path)
assert len(argv) == 2
model_path = argv[1]
build_mock_model(model_path)
if __name__ == '__main__':
main(sys.argv)
if __name__ == "__main__":
main(sys.argv)

View File

@ -20,68 +20,76 @@ import subprocess
from typing import Callable, List, Union
def send(f: io.BufferedWriter, value: Union[int, float],
spec: log_reader.TensorSpec):
"""Send the `value` - currently just a scalar - formatted as per `spec`."""
def send(f: io.BufferedWriter, value: Union[int, float], spec: log_reader.TensorSpec):
"""Send the `value` - currently just a scalar - formatted as per `spec`."""
# just int64 for now
assert (spec.element_type == ctypes.c_int64)
to_send = ctypes.c_int64(int(value))
assert f.write(bytes(to_send)) == ctypes.sizeof(
spec.element_type) * math.prod(spec.shape)
f.flush()
# just int64 for now
assert spec.element_type == ctypes.c_int64
to_send = ctypes.c_int64(int(value))
assert f.write(bytes(to_send)) == ctypes.sizeof(spec.element_type) * math.prod(
spec.shape
)
f.flush()
def run_interactive(temp_rootname: str,
make_response: Callable[[List[log_reader.TensorValue]],
Union[int, float]],
process_and_args: List[str]):
"""Host the compiler.
Args:
temp_rootname: the base file name from which to construct the 2 pipes for
communicating with the compiler.
make_response: a function that, given the current tensor values, provides a
response.
process_and_args: the full commandline for the compiler. It it assumed it
contains a flag poiting to `temp_rootname` so that the InteractiveModeRunner
would attempt communication on the same pair as this function opens.
def run_interactive(
temp_rootname: str,
make_response: Callable[[List[log_reader.TensorValue]], Union[int, float]],
process_and_args: List[str],
):
"""Host the compiler.
Args:
temp_rootname: the base file name from which to construct the 2 pipes for
communicating with the compiler.
make_response: a function that, given the current tensor values, provides a
response.
process_and_args: the full commandline for the compiler. It it assumed it
contains a flag poiting to `temp_rootname` so that the InteractiveModeRunner
would attempt communication on the same pair as this function opens.
This function sets up the communication with the compiler - via 2 files named
`temp_rootname`.in and `temp_rootname`.out - prints out the received features,
and sends back to the compiler an advice (which it gets from `make_response`).
It's used for testing, and also to showcase how to set up communication in an
interactive ML ("gym") environment.
"""
to_compiler = temp_rootname + ".in"
from_compiler = temp_rootname + ".out"
try:
os.mkfifo(to_compiler, 0o666)
os.mkfifo(from_compiler, 0o666)
compiler_proc = subprocess.Popen(
process_and_args, stderr=subprocess.PIPE, stdout=subprocess.DEVNULL)
with io.BufferedWriter(io.FileIO(to_compiler, 'wb')) as tc:
with io.BufferedReader(io.FileIO(from_compiler, 'rb')) as fc:
tensor_specs, _, advice_spec = log_reader.read_header(fc)
context = None
while compiler_proc.poll() is None:
next_event = fc.readline()
if not next_event:
break
last_context, observation_id, features, _ = log_reader.read_one_observation(
context, next_event, fc, tensor_specs, None)
if last_context != context:
print(f'context: {last_context}')
context = last_context
print(f'observation: {observation_id}')
tensor_values = []
for fv in features:
log_reader.pretty_print_tensor_value(fv)
tensor_values.append(fv)
send(tc, make_response(tensor_values), advice_spec)
_, err = compiler_proc.communicate()
print(err.decode('utf-8'))
compiler_proc.wait()
This function sets up the communication with the compiler - via 2 files named
`temp_rootname`.in and `temp_rootname`.out - prints out the received features,
and sends back to the compiler an advice (which it gets from `make_response`).
It's used for testing, and also to showcase how to set up communication in an
interactive ML ("gym") environment.
"""
to_compiler = temp_rootname + ".in"
from_compiler = temp_rootname + ".out"
try:
os.mkfifo(to_compiler, 0o666)
os.mkfifo(from_compiler, 0o666)
compiler_proc = subprocess.Popen(
process_and_args, stderr=subprocess.PIPE, stdout=subprocess.DEVNULL
)
with io.BufferedWriter(io.FileIO(to_compiler, "wb")) as tc:
with io.BufferedReader(io.FileIO(from_compiler, "rb")) as fc:
tensor_specs, _, advice_spec = log_reader.read_header(fc)
context = None
while compiler_proc.poll() is None:
next_event = fc.readline()
if not next_event:
break
(
last_context,
observation_id,
features,
_,
) = log_reader.read_one_observation(
context, next_event, fc, tensor_specs, None
)
if last_context != context:
print(f"context: {last_context}")
context = last_context
print(f"observation: {observation_id}")
tensor_values = []
for fv in features:
log_reader.pretty_print_tensor_value(fv)
tensor_values.append(fv)
send(tc, make_response(tensor_values), advice_spec)
_, err = compiler_proc.communicate()
print(err.decode("utf-8"))
compiler_proc.wait()
finally:
os.unlink(to_compiler)
os.unlink(from_compiler)
finally:
os.unlink(to_compiler)
os.unlink(from_compiler)

View File

@ -11,128 +11,130 @@ import sys
from typing import List, Optional
_element_types = {
'float': ctypes.c_float,
'double': ctypes.c_double,
'int8_t': ctypes.c_int8,
'uint8_t': ctypes.c_uint8,
'int16_t': ctypes.c_int16,
'uint16_t': ctypes.c_uint16,
'int32_t': ctypes.c_int32,
'uint32_t': ctypes.c_uint32,
'int64_t': ctypes.c_int64,
'uint64_t': ctypes.c_uint64
"float": ctypes.c_float,
"double": ctypes.c_double,
"int8_t": ctypes.c_int8,
"uint8_t": ctypes.c_uint8,
"int16_t": ctypes.c_int16,
"uint16_t": ctypes.c_uint16,
"int32_t": ctypes.c_int32,
"uint32_t": ctypes.c_uint32,
"int64_t": ctypes.c_int64,
"uint64_t": ctypes.c_uint64,
}
@dataclasses.dataclass(frozen=True)
class TensorSpec:
name: str
port: int
shape: List[int]
element_type: type
name: str
port: int
shape: List[int]
element_type: type
@staticmethod
def from_dict(d: dict):
name = d['name']
port = d['port']
shape = [int(e) for e in d['shape']]
element_type_str = d['type']
if element_type_str not in _element_types:
raise ValueError(f'uknown type: {element_type_str}')
return TensorSpec(
name=name,
port=port,
shape=shape,
element_type=_element_types[element_type_str])
@staticmethod
def from_dict(d: dict):
name = d["name"]
port = d["port"]
shape = [int(e) for e in d["shape"]]
element_type_str = d["type"]
if element_type_str not in _element_types:
raise ValueError(f"uknown type: {element_type_str}")
return TensorSpec(
name=name,
port=port,
shape=shape,
element_type=_element_types[element_type_str],
)
class TensorValue:
def __init__(self, spec: TensorSpec, buffer: bytes):
self._spec = spec
self._buffer = buffer
self._view = ctypes.cast(self._buffer, ctypes.POINTER(self._spec.element_type))
self._len = math.prod(self._spec.shape)
def __init__(self, spec: TensorSpec, buffer: bytes):
self._spec = spec
self._buffer = buffer
self._view = ctypes.cast(self._buffer,
ctypes.POINTER(self._spec.element_type))
self._len = math.prod(self._spec.shape)
def spec(self) -> TensorSpec:
return self._spec
def spec(self) -> TensorSpec:
return self._spec
def __len__(self) -> int:
return self._len
def __len__(self) -> int:
return self._len
def __getitem__(self, index):
if index < 0 or index >= self._len:
raise IndexError(f'Index {index} out of range [0..{self._len})')
return self._view[index]
def __getitem__(self, index):
if index < 0 or index >= self._len:
raise IndexError(f"Index {index} out of range [0..{self._len})")
return self._view[index]
def read_tensor(fs: io.BufferedReader, ts: TensorSpec) -> TensorValue:
size = math.prod(ts.shape) * ctypes.sizeof(ts.element_type)
data = fs.read(size)
return TensorValue(ts, data)
size = math.prod(ts.shape) * ctypes.sizeof(ts.element_type)
data = fs.read(size)
return TensorValue(ts, data)
def pretty_print_tensor_value(tv: TensorValue):
print(f'{tv.spec().name}: {",".join([str(v) for v in tv])}')
print(f'{tv.spec().name}: {",".join([str(v) for v in tv])}')
def read_header(f: io.BufferedReader):
header = json.loads(f.readline())
tensor_specs = [TensorSpec.from_dict(ts) for ts in header['features']]
score_spec = TensorSpec.from_dict(
header['score']) if 'score' in header else None
advice_spec = TensorSpec.from_dict(
header['advice']) if 'advice' in header else None
return tensor_specs, score_spec, advice_spec
header = json.loads(f.readline())
tensor_specs = [TensorSpec.from_dict(ts) for ts in header["features"]]
score_spec = TensorSpec.from_dict(header["score"]) if "score" in header else None
advice_spec = TensorSpec.from_dict(header["advice"]) if "advice" in header else None
return tensor_specs, score_spec, advice_spec
def read_one_observation(context: Optional[str], event_str: str,
f: io.BufferedReader, tensor_specs: List[TensorSpec],
score_spec: Optional[TensorSpec]):
event = json.loads(event_str)
if 'context' in event:
context = event['context']
event = json.loads(f.readline())
observation_id = int(event['observation'])
features = []
for ts in tensor_specs:
features.append(read_tensor(f, ts))
f.readline()
score = None
if score_spec is not None:
score_header = json.loads(f.readline())
assert int(score_header['outcome']) == observation_id
score = read_tensor(f, score_spec)
def read_one_observation(
context: Optional[str],
event_str: str,
f: io.BufferedReader,
tensor_specs: List[TensorSpec],
score_spec: Optional[TensorSpec],
):
event = json.loads(event_str)
if "context" in event:
context = event["context"]
event = json.loads(f.readline())
observation_id = int(event["observation"])
features = []
for ts in tensor_specs:
features.append(read_tensor(f, ts))
f.readline()
return context, observation_id, features, score
score = None
if score_spec is not None:
score_header = json.loads(f.readline())
assert int(score_header["outcome"]) == observation_id
score = read_tensor(f, score_spec)
f.readline()
return context, observation_id, features, score
def read_stream(fname: str):
with io.BufferedReader(io.FileIO(fname, 'rb')) as f:
tensor_specs, score_spec, _ = read_header(f)
context = None
while True:
event_str = f.readline()
if not event_str:
break
context, observation_id, features, score = read_one_observation(
context, event_str, f, tensor_specs, score_spec)
yield context, observation_id, features, score
with io.BufferedReader(io.FileIO(fname, "rb")) as f:
tensor_specs, score_spec, _ = read_header(f)
context = None
while True:
event_str = f.readline()
if not event_str:
break
context, observation_id, features, score = read_one_observation(
context, event_str, f, tensor_specs, score_spec
)
yield context, observation_id, features, score
def main(args):
last_context = None
for ctx, obs_id, features, score in read_stream(args[1]):
if last_context != ctx:
print(f'context: {ctx}')
last_context = ctx
print(f'observation: {obs_id}')
for fv in features:
pretty_print_tensor_value(fv)
if score:
pretty_print_tensor_value(score)
last_context = None
for ctx, obs_id, features, score in read_stream(args[1]):
if last_context != ctx:
print(f"context: {ctx}")
last_context = ctx
print(f"observation: {obs_id}")
for fv in features:
pretty_print_tensor_value(fv)
if score:
pretty_print_tensor_value(score)
if __name__ == '__main__':
main(sys.argv)
if __name__ == "__main__":
main(sys.argv)

View File

@ -14,24 +14,24 @@ from tf_agents.policies import greedy_policy
def main(argv):
assert len(argv) == 3
sm_dir = argv[1]
tfl_dir = argv[2]
tf.io.gfile.makedirs(tfl_dir)
tfl_path = os.path.join(tfl_dir, 'model.tflite')
converter = tf.lite.TFLiteConverter.from_saved_model(sm_dir)
converter.target_spec.supported_ops = [
tf.lite.OpsSet.TFLITE_BUILTINS,
]
tfl_model = converter.convert()
with tf.io.gfile.GFile(tfl_path, 'wb') as f:
f.write(tfl_model)
json_file = 'output_spec.json'
src_json = os.path.join(sm_dir, json_file)
if tf.io.gfile.exists(src_json):
tf.io.gfile.copy(src_json,
os.path.join(tfl_dir, json_file))
assert len(argv) == 3
sm_dir = argv[1]
tfl_dir = argv[2]
tf.io.gfile.makedirs(tfl_dir)
tfl_path = os.path.join(tfl_dir, "model.tflite")
converter = tf.lite.TFLiteConverter.from_saved_model(sm_dir)
converter.target_spec.supported_ops = [
tf.lite.OpsSet.TFLITE_BUILTINS,
]
tfl_model = converter.convert()
with tf.io.gfile.GFile(tfl_path, "wb") as f:
f.write(tfl_model)
if __name__ == '__main__':
main(sys.argv)
json_file = "output_spec.json"
src_json = os.path.join(sm_dir, json_file)
if tf.io.gfile.exists(src_json):
tf.io.gfile.copy(src_json, os.path.join(tfl_dir, json_file))
if __name__ == "__main__":
main(sys.argv)

View File

@ -7,6 +7,6 @@ import sys
# Currently any print-out from the custom tool is interpreted as a crash
# (i.e. test is still interesting)
print("Error: " + ' '.join(sys.argv[1:]))
print("Error: " + " ".join(sys.argv[1:]))
sys.exit(1)

View File

@ -2,13 +2,14 @@
import textwrap
import enum
import os
"""
Generate the tests in llvm/test/CodeGen/AArch64/Atomics. Run from top level llvm-project.
"""
TRIPLES = [
'aarch64',
'aarch64_be',
"aarch64",
"aarch64_be",
]
@ -117,28 +118,28 @@ class Feature(enum.Flag):
@property
def mattr(self):
if self == Feature.outline_atomics:
return '+outline-atomics'
return "+outline-atomics"
if self == Feature.v8_1a:
return '+v8.1a'
return "+v8.1a"
if self == Feature.rcpc3:
return '+lse2,+rcpc3'
return "+lse2,+rcpc3"
if self == Feature.lse2_lse128:
return '+lse2,+lse128'
return '+' + self.name
return "+lse2,+lse128"
return "+" + self.name
ATOMICRMW_OPS = [
'xchg',
'add',
'sub',
'and',
'nand',
'or',
'xor',
'max',
'min',
'umax',
'umin',
"xchg",
"add",
"sub",
"and",
"nand",
"or",
"xor",
"max",
"min",
"umax",
"umin",
]
@ -147,15 +148,18 @@ def all_atomicrmw(f):
for aligned in Aligned:
for ty in Type:
for ordering in ATOMICRMW_ORDERS:
name = f'atomicrmw_{op}_{ty}_{aligned}_{ordering}'
instr = 'atomicrmw'
name = f"atomicrmw_{op}_{ty}_{aligned}_{ordering}"
instr = "atomicrmw"
f.write(
textwrap.dedent(f'''
textwrap.dedent(
f"""
define dso_local {ty} @{name}(ptr %ptr, {ty} %value) {{
%r = {instr} {op} ptr %ptr, {ty} %value {ordering}, align {ty.align(aligned)}
ret {ty} %r
}}
'''))
"""
)
)
def all_load(f):
@ -163,33 +167,39 @@ def all_load(f):
for ty in Type:
for ordering in ATOMIC_LOAD_ORDERS:
for const in [False, True]:
name = f'load_atomic_{ty}_{aligned}_{ordering}'
instr = 'load atomic'
name = f"load_atomic_{ty}_{aligned}_{ordering}"
instr = "load atomic"
if const:
name += '_const'
arg = 'ptr readonly %ptr' if const else 'ptr %ptr'
name += "_const"
arg = "ptr readonly %ptr" if const else "ptr %ptr"
f.write(
textwrap.dedent(f'''
textwrap.dedent(
f"""
define dso_local {ty} @{name}({arg}) {{
%r = {instr} {ty}, ptr %ptr {ordering}, align {ty.align(aligned)}
ret {ty} %r
}}
'''))
"""
)
)
def all_store(f):
for aligned in Aligned:
for ty in Type:
for ordering in ATOMIC_STORE_ORDERS: # FIXME stores
name = f'store_atomic_{ty}_{aligned}_{ordering}'
instr = 'store atomic'
name = f"store_atomic_{ty}_{aligned}_{ordering}"
instr = "store atomic"
f.write(
textwrap.dedent(f'''
textwrap.dedent(
f"""
define dso_local void @{name}({ty} %value, ptr %ptr) {{
{instr} {ty} %value, ptr %ptr {ordering}, align {ty.align(aligned)}
ret void
}}
'''))
"""
)
)
def all_cmpxchg(f):
@ -198,85 +208,113 @@ def all_cmpxchg(f):
for success_ordering in CMPXCHG_SUCCESS_ORDERS:
for failure_ordering in CMPXCHG_FAILURE_ORDERS:
for weak in [False, True]:
name = f'cmpxchg_{ty}_{aligned}_{success_ordering}_{failure_ordering}'
instr = 'cmpxchg'
name = f"cmpxchg_{ty}_{aligned}_{success_ordering}_{failure_ordering}"
instr = "cmpxchg"
if weak:
name += '_weak'
instr += ' weak'
name += "_weak"
instr += " weak"
f.write(
textwrap.dedent(f'''
textwrap.dedent(
f"""
define dso_local {ty} @{name}({ty} %expected, {ty} %new, ptr %ptr) {{
%pair = {instr} ptr %ptr, {ty} %expected, {ty} %new {success_ordering} {failure_ordering}, align {ty.align(aligned)}
%r = extractvalue {{ {ty}, i1 }} %pair, 0
ret {ty} %r
}}
'''))
"""
)
)
def all_fence(f):
for ordering in FENCE_ORDERS:
name = f'fence_{ordering}'
name = f"fence_{ordering}"
f.write(
textwrap.dedent(f'''
textwrap.dedent(
f"""
define dso_local void @{name}() {{
fence {ordering}
ret void
}}
'''))
"""
)
)
def header(f, triple, features, filter_args: str):
f.write('; NOTE: Assertions have been autogenerated by '
'utils/update_llc_test_checks.py UTC_ARGS: ')
f.write(
"; NOTE: Assertions have been autogenerated by "
"utils/update_llc_test_checks.py UTC_ARGS: "
)
f.write(filter_args)
f.write('\n')
f.write(f'; The base test file was generated by {__file__}\n')
f.write("\n")
f.write(f"; The base test file was generated by {__file__}\n")
for feat in features:
for OptFlag in ['-O0', '-O1']:
f.write(' '.join([
';', 'RUN:', 'llc', '%s', '-o', '-', '-verify-machineinstrs',
f'-mtriple={triple}', f'-mattr={feat.mattr}', OptFlag, '|',
'FileCheck', '%s', f'--check-prefixes=CHECK,{OptFlag}\n'
]))
for OptFlag in ["-O0", "-O1"]:
f.write(
" ".join(
[
";",
"RUN:",
"llc",
"%s",
"-o",
"-",
"-verify-machineinstrs",
f"-mtriple={triple}",
f"-mattr={feat.mattr}",
OptFlag,
"|",
"FileCheck",
"%s",
f"--check-prefixes=CHECK,{OptFlag}\n",
]
)
)
def write_lit_tests():
os.chdir('llvm/test/CodeGen/AArch64/Atomics/')
os.chdir("llvm/test/CodeGen/AArch64/Atomics/")
for triple in TRIPLES:
# Feature has no effect on fence, so keep it to one file.
with open(f'{triple}-fence.ll', 'w') as f:
with open(f"{triple}-fence.ll", "w") as f:
filter_args = r'--filter "^\s*(dmb)"'
header(f, triple, Feature, filter_args)
all_fence(f)
for feat in Feature:
with open(f'{triple}-atomicrmw-{feat.name}.ll', 'w') as f:
with open(f"{triple}-atomicrmw-{feat.name}.ll", "w") as f:
filter_args = r'--filter-out "\b(sp)\b" --filter "^\s*(ld[^r]|st[^r]|swp|cas|bl|add|and|eor|orn|orr|sub|mvn|sxt|cmp|ccmp|csel|dmb)"'
header(f, triple, [feat], filter_args)
all_atomicrmw(f)
with open(f'{triple}-cmpxchg-{feat.name}.ll', 'w') as f:
with open(f"{triple}-cmpxchg-{feat.name}.ll", "w") as f:
filter_args = r'--filter-out "\b(sp)\b" --filter "^\s*(ld[^r]|st[^r]|swp|cas|bl|add|and|eor|orn|orr|sub|mvn|sxt|cmp|ccmp|csel|dmb)"'
header(f, triple, [feat], filter_args)
all_cmpxchg(f)
with open(f'{triple}-atomic-load-{feat.name}.ll', 'w') as f:
with open(f"{triple}-atomic-load-{feat.name}.ll", "w") as f:
filter_args = r'--filter-out "\b(sp)\b" --filter "^\s*(ld|st[^r]|swp|cas|bl|add|and|eor|orn|orr|sub|mvn|sxt|cmp|ccmp|csel|dmb)"'
header(f, triple, [feat], filter_args)
all_load(f)
with open(f'{triple}-atomic-store-{feat.name}.ll', 'w') as f:
with open(f"{triple}-atomic-store-{feat.name}.ll", "w") as f:
filter_args = r'--filter-out "\b(sp)\b" --filter "^\s*(ld[^r]|st|swp|cas|bl|add|and|eor|orn|orr|sub|mvn|sxt|cmp|ccmp|csel|dmb)"'
header(f, triple, [feat], filter_args)
all_store(f)
if __name__ == '__main__':
if __name__ == "__main__":
write_lit_tests()
print(textwrap.dedent('''
print(
textwrap.dedent(
"""
Testcases written. To update checks run:
$ ./llvm/utils/update_llc_test_checks.py -u llvm/test/CodeGen/AArch64/Atomics/*.ll
Or in parallel:
$ parallel ./llvm/utils/update_llc_test_checks.py -u ::: llvm/test/CodeGen/AArch64/Atomics/*.ll
'''))
"""
)
)

View File

@ -4,25 +4,24 @@ import sys
def main(args):
# this advisor just picks the first legal register to evict, which is
# identifiable by the "mask" feature
class Advisor:
to_return = False
# this advisor just picks the first legal register to evict, which is
# identifiable by the "mask" feature
class Advisor:
to_return = False
def advice(self, tensor_values: list[log_reader.TensorValue]):
for tv in tensor_values:
if tv.spec().name != 'mask':
continue
for i, v in enumerate(tv):
if v == 1:
return i
# i.e. invalid:
return -1
def advice(self, tensor_values: list[log_reader.TensorValue]):
for tv in tensor_values:
if tv.spec().name != "mask":
continue
for i, v in enumerate(tv):
if v == 1:
return i
# i.e. invalid:
return -1
a = Advisor()
interactive_host.run_interactive(args[0], a.advice, args[1:])
a = Advisor()
interactive_host.run_interactive(args[0], a.advice, args[1:])
if __name__ == '__main__':
main(sys.argv[1:])
if __name__ == "__main__":
main(sys.argv[1:])

View File

@ -20,7 +20,7 @@ llvm_type_to_ptx_type = {
"half": "b16",
"<2 x half>": "b32",
"float": "f32",
"double": "f64"
"double": "f64",
}
llvm_type_to_ptx_reg = {
@ -31,7 +31,7 @@ llvm_type_to_ptx_reg = {
"half": "h",
"<2 x half>": "hh",
"float": "f",
"double": "fd"
"double": "fd",
}
addrspace_id = {
@ -40,12 +40,12 @@ addrspace_id = {
".shared": 3,
".const": 4,
".local": 5,
".param": 101
".param": 101,
}
def gen_load_tests():
load_template = """
load_template = """
define ${type} @${testname}(${type} addrspace(${asid})* %ptr) {
; CHECK: ${testname}
; CHECK_P32: ld${_volatile}${_volatile_as}.${ptx_type} %${ptx_reg}{{[0-9]+}}, [%r{{[0-9]+}}]
@ -56,51 +56,52 @@ define ${type} @${testname}(${type} addrspace(${asid})* %ptr) {
ret ${type} %a
}
"""
for op_type, volatile, space in product(
["i8", "i16", "i32", "i64", "half", "float", "double", "<2 x half>"],
[True, False], # volatile
["", ".shared", ".global", ".const", ".local", ".param"]):
for op_type, volatile, space in product(
["i8", "i16", "i32", "i64", "half", "float", "double", "<2 x half>"],
[True, False], # volatile
["", ".shared", ".global", ".const", ".local", ".param"],
):
# Volatile is only supported for global, shared and generic.
if volatile and not space in ["", ".global", ".shared"]:
continue
# Volatile is only supported for global, shared and generic.
if volatile and not space in ["", ".global", ".shared"]:
continue
# Volatile is only supported for global, shared and generic.
# All other volatile accesses are done in generic AS.
if volatile and not space in ["", ".global", ".shared"]:
volatile_as = ""
else:
volatile_as = space
# Volatile is only supported for global, shared and generic.
# All other volatile accesses are done in generic AS.
if volatile and not space in ["", ".global", ".shared"]:
volatile_as = ""
else:
volatile_as = space
params = {
"type": op_type,
"volatile": "volatile" if volatile else "",
"_volatile": ".volatile" if volatile else "",
"_volatile_as": volatile_as,
"_space": space,
"ptx_reg": llvm_type_to_ptx_reg[op_type],
"ptx_type": llvm_type_to_ptx_type[op_type],
"asid": addrspace_id[space],
}
params = {
"type": op_type,
"volatile": "volatile" if volatile else "",
"_volatile": ".volatile" if volatile else "",
"_volatile_as": volatile_as,
"_space": space,
"ptx_reg": llvm_type_to_ptx_reg[op_type],
"ptx_type": llvm_type_to_ptx_type[op_type],
"asid": addrspace_id[space],
}
testname = \
Template("ld_${_volatile}${_space}.${ptx_type}").substitute(params)
params["testname"] = testname.replace(".", "_")
testname = Template("ld_${_volatile}${_space}.${ptx_type}").substitute(params)
params["testname"] = testname.replace(".", "_")
# LLVM does not accept "addrspacecast Type* addrspace(0) to Type*", so we
# need to avoid it for generic pointer tests.
if space:
generic_ptr_template = ("addrspacecast ${type} addrspace(${asid})* %ptr "
"to ${type}*")
else:
generic_ptr_template = "select i1 true, ${type}* %ptr, ${type}* %ptr"
params["generic_ptr"] = Template(generic_ptr_template).substitute(params)
# LLVM does not accept "addrspacecast Type* addrspace(0) to Type*", so we
# need to avoid it for generic pointer tests.
if space:
generic_ptr_template = (
"addrspacecast ${type} addrspace(${asid})* %ptr " "to ${type}*"
)
else:
generic_ptr_template = "select i1 true, ${type}* %ptr, ${type}* %ptr"
params["generic_ptr"] = Template(generic_ptr_template).substitute(params)
print(Template(load_template).substitute(params))
print(Template(load_template).substitute(params))
def main():
gen_load_tests()
gen_load_tests()
main()

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -70,41 +70,41 @@
from __future__ import print_function
branch_blocks = 10
main_size = 0xffd8
main_size = 0xFFD8
print('@global = global i32 0')
print("@global = global i32 0")
print('define void @f1(i8 *%base, i32 *%stop, i32 %limit) {')
print('entry:')
print(' br label %before0')
print('')
print("define void @f1(i8 *%base, i32 *%stop, i32 %limit) {")
print("entry:")
print(" br label %before0")
print("")
for i in range(branch_blocks):
next = 'before%d' % (i + 1) if i + 1 < branch_blocks else 'main'
print('before%d:' % i)
print(' %%bstop%d = getelementptr i32, i32 *%%stop, i64 %d' % (i, i))
print(' %%bcur%d = load i32 , i32 *%%bstop%d' % (i, i))
print(' %%btest%d = icmp eq i32 %%limit, %%bcur%d' % (i, i))
print(' br i1 %%btest%d, label %%after0, label %%%s' % (i, next))
print('')
next = "before%d" % (i + 1) if i + 1 < branch_blocks else "main"
print("before%d:" % i)
print(" %%bstop%d = getelementptr i32, i32 *%%stop, i64 %d" % (i, i))
print(" %%bcur%d = load i32 , i32 *%%bstop%d" % (i, i))
print(" %%btest%d = icmp eq i32 %%limit, %%bcur%d" % (i, i))
print(" br i1 %%btest%d, label %%after0, label %%%s" % (i, next))
print("")
print('%s:' % next)
print("%s:" % next)
a, b = 1, 1
for i in range(0, main_size, 6):
a, b = b, a + b
offset = 4096 + b % 500000
value = a % 256
print(' %%ptr%d = getelementptr i8, i8 *%%base, i64 %d' % (i, offset))
print(' store volatile i8 %d, i8 *%%ptr%d' % (value, i))
print(" %%ptr%d = getelementptr i8, i8 *%%base, i64 %d" % (i, offset))
print(" store volatile i8 %d, i8 *%%ptr%d" % (value, i))
for i in range(branch_blocks):
print(' %%astop%d = getelementptr i32, i32 *%%stop, i64 %d' % (i, i + 25))
print(' %%acur%d = load i32 , i32 *%%astop%d' % (i, i))
print(' %%atest%d = icmp eq i32 %%limit, %%acur%d' % (i, i))
print(' br i1 %%atest%d, label %%main, label %%after%d' % (i, i))
print('')
print('after%d:' % i)
print(" %%astop%d = getelementptr i32, i32 *%%stop, i64 %d" % (i, i + 25))
print(" %%acur%d = load i32 , i32 *%%astop%d" % (i, i))
print(" %%atest%d = icmp eq i32 %%limit, %%acur%d" % (i, i))
print(" br i1 %%atest%d, label %%main, label %%after%d" % (i, i))
print("")
print("after%d:" % i)
print(' %dummy = load volatile i32, i32 *@global')
print(' ret void')
print('}')
print(" %dummy = load volatile i32, i32 *@global")
print(" ret void")
print("}")

View File

@ -60,25 +60,25 @@ from __future__ import print_function
blocks = 256 + 4
print('define void @f1(i8 *%base, i32 *%stop, i32 %limit) {')
print('entry:')
print(' br label %b0')
print('')
print("define void @f1(i8 *%base, i32 *%stop, i32 %limit) {")
print("entry:")
print(" br label %b0")
print("")
a, b = 1, 1
for i in range(blocks):
a, b = b, a + b
value = a % 256
next = 'b%d' % (i + 1) if i + 1 < blocks else 'end'
other = 'end' if 2 * i < blocks else 'b0'
print('b%d:' % i)
print(' store volatile i8 %d, i8 *%%base' % value)
print(' %%astop%d = getelementptr i32, i32 *%%stop, i64 %d' % (i, i))
print(' %%acur%d = load i32 , i32 *%%astop%d' % (i, i))
print(' %%atest%d = icmp eq i32 %%limit, %%acur%d' % (i, i))
print(' br i1 %%atest%d, label %%%s, label %%%s' % (i, other, next))
next = "b%d" % (i + 1) if i + 1 < blocks else "end"
other = "end" if 2 * i < blocks else "b0"
print("b%d:" % i)
print(" store volatile i8 %d, i8 *%%base" % value)
print(" %%astop%d = getelementptr i32, i32 *%%stop, i64 %d" % (i, i))
print(" %%acur%d = load i32 , i32 *%%astop%d" % (i, i))
print(" %%atest%d = icmp eq i32 %%limit, %%acur%d" % (i, i))
print(" br i1 %%atest%d, label %%%s, label %%%s" % (i, other, next))
print('')
print('%s:' % next)
print(' ret void')
print('}')
print("")
print("%s:" % next)
print(" ret void")
print("}")

View File

@ -70,43 +70,43 @@
from __future__ import print_function
branch_blocks = 8
main_size = 0xffcc
main_size = 0xFFCC
print('@global = global i32 0')
print("@global = global i32 0")
print('define void @f1(i8 *%base, i8 *%stop, i32 %limit) {')
print('entry:')
print(' br label %before0')
print('')
print("define void @f1(i8 *%base, i8 *%stop, i32 %limit) {")
print("entry:")
print(" br label %before0")
print("")
for i in range(branch_blocks):
next = 'before%d' % (i + 1) if i + 1 < branch_blocks else 'main'
print('before%d:' % i)
print(' %%bstop%d = getelementptr i8, i8 *%%stop, i64 %d' % (i, i))
print(' %%bcur%d = load i8 , i8 *%%bstop%d' % (i, i))
print(' %%bext%d = sext i8 %%bcur%d to i32' % (i, i))
print(' %%btest%d = icmp eq i32 %%limit, %%bext%d' % (i, i))
print(' br i1 %%btest%d, label %%after0, label %%%s' % (i, next))
print('')
next = "before%d" % (i + 1) if i + 1 < branch_blocks else "main"
print("before%d:" % i)
print(" %%bstop%d = getelementptr i8, i8 *%%stop, i64 %d" % (i, i))
print(" %%bcur%d = load i8 , i8 *%%bstop%d" % (i, i))
print(" %%bext%d = sext i8 %%bcur%d to i32" % (i, i))
print(" %%btest%d = icmp eq i32 %%limit, %%bext%d" % (i, i))
print(" br i1 %%btest%d, label %%after0, label %%%s" % (i, next))
print("")
print('%s:' % next)
print("%s:" % next)
a, b = 1, 1
for i in range(0, main_size, 6):
a, b = b, a + b
offset = 4096 + b % 500000
value = a % 256
print(' %%ptr%d = getelementptr i8, i8 *%%base, i64 %d' % (i, offset))
print(' store volatile i8 %d, i8 *%%ptr%d' % (value, i))
print(" %%ptr%d = getelementptr i8, i8 *%%base, i64 %d" % (i, offset))
print(" store volatile i8 %d, i8 *%%ptr%d" % (value, i))
for i in range(branch_blocks):
print(' %%astop%d = getelementptr i8, i8 *%%stop, i64 %d' % (i, i + 25))
print(' %%acur%d = load i8 , i8 *%%astop%d' % (i, i))
print(' %%aext%d = sext i8 %%acur%d to i32' % (i, i))
print(' %%atest%d = icmp eq i32 %%limit, %%aext%d' % (i, i))
print(' br i1 %%atest%d, label %%main, label %%after%d' % (i, i))
print('')
print('after%d:' % i)
print(" %%astop%d = getelementptr i8, i8 *%%stop, i64 %d" % (i, i + 25))
print(" %%acur%d = load i8 , i8 *%%astop%d" % (i, i))
print(" %%aext%d = sext i8 %%acur%d to i32" % (i, i))
print(" %%atest%d = icmp eq i32 %%limit, %%aext%d" % (i, i))
print(" br i1 %%atest%d, label %%main, label %%after%d" % (i, i))
print("")
print("after%d:" % i)
print(' %dummy = load volatile i32, i32 *@global')
print(' ret void')
print('}')
print(" %dummy = load volatile i32, i32 *@global")
print(" ret void")
print("}")

View File

@ -74,43 +74,43 @@
from __future__ import print_function
branch_blocks = 8
main_size = 0xffcc
main_size = 0xFFCC
print('@global = global i32 0')
print("@global = global i32 0")
print('define void @f1(i8 *%base, i8 *%stop, i64 %limit) {')
print('entry:')
print(' br label %before0')
print('')
print("define void @f1(i8 *%base, i8 *%stop, i64 %limit) {")
print("entry:")
print(" br label %before0")
print("")
for i in range(branch_blocks):
next = 'before%d' % (i + 1) if i + 1 < branch_blocks else 'main'
print('before%d:' % i)
print(' %%bstop%d = getelementptr i8, i8 *%%stop, i64 %d' % (i, i))
print(' %%bcur%d = load i8 , i8 *%%bstop%d' % (i, i))
print(' %%bext%d = sext i8 %%bcur%d to i64' % (i, i))
print(' %%btest%d = icmp eq i64 %%limit, %%bext%d' % (i, i))
print(' br i1 %%btest%d, label %%after0, label %%%s' % (i, next))
print('')
next = "before%d" % (i + 1) if i + 1 < branch_blocks else "main"
print("before%d:" % i)
print(" %%bstop%d = getelementptr i8, i8 *%%stop, i64 %d" % (i, i))
print(" %%bcur%d = load i8 , i8 *%%bstop%d" % (i, i))
print(" %%bext%d = sext i8 %%bcur%d to i64" % (i, i))
print(" %%btest%d = icmp eq i64 %%limit, %%bext%d" % (i, i))
print(" br i1 %%btest%d, label %%after0, label %%%s" % (i, next))
print("")
print('%s:' % next)
print("%s:" % next)
a, b = 1, 1
for i in range(0, main_size, 6):
a, b = b, a + b
offset = 4096 + b % 500000
value = a % 256
print(' %%ptr%d = getelementptr i8, i8 *%%base, i64 %d' % (i, offset))
print(' store volatile i8 %d, i8 *%%ptr%d' % (value, i))
print(" %%ptr%d = getelementptr i8, i8 *%%base, i64 %d" % (i, offset))
print(" store volatile i8 %d, i8 *%%ptr%d" % (value, i))
for i in range(branch_blocks):
print(' %%astop%d = getelementptr i8, i8 *%%stop, i64 %d' % (i, i + 25))
print(' %%acur%d = load i8 , i8 *%%astop%d' % (i, i))
print(' %%aext%d = sext i8 %%acur%d to i64' % (i, i))
print(' %%atest%d = icmp eq i64 %%limit, %%aext%d' % (i, i))
print(' br i1 %%atest%d, label %%main, label %%after%d' % (i, i))
print('')
print('after%d:' % i)
print(" %%astop%d = getelementptr i8, i8 *%%stop, i64 %d" % (i, i + 25))
print(" %%acur%d = load i8 , i8 *%%astop%d" % (i, i))
print(" %%aext%d = sext i8 %%acur%d to i64" % (i, i))
print(" %%atest%d = icmp eq i64 %%limit, %%aext%d" % (i, i))
print(" br i1 %%atest%d, label %%main, label %%after%d" % (i, i))
print("")
print("after%d:" % i)
print(' %dummy = load volatile i32, i32 *@global')
print(' ret void')
print('}')
print(" %dummy = load volatile i32, i32 *@global")
print(" ret void")
print("}")

View File

@ -74,41 +74,41 @@
from __future__ import print_function
branch_blocks = 8
main_size = 0xffcc
main_size = 0xFFCC
print('@global = global i32 0')
print("@global = global i32 0")
print('define void @f1(i8 *%base, i8 *%stop) {')
print('entry:')
print(' br label %before0')
print('')
print("define void @f1(i8 *%base, i8 *%stop) {")
print("entry:")
print(" br label %before0")
print("")
for i in range(branch_blocks):
next = 'before%d' % (i + 1) if i + 1 < branch_blocks else 'main'
print('before%d:' % i)
print(' %%bcur%d = load i8 , i8 *%%stop' % i)
print(' %%bext%d = sext i8 %%bcur%d to i32' % (i, i))
print(' %%btest%d = icmp slt i32 %%bext%d, %d' % (i, i, i + 50))
print(' br i1 %%btest%d, label %%after0, label %%%s' % (i, next))
print('')
next = "before%d" % (i + 1) if i + 1 < branch_blocks else "main"
print("before%d:" % i)
print(" %%bcur%d = load i8 , i8 *%%stop" % i)
print(" %%bext%d = sext i8 %%bcur%d to i32" % (i, i))
print(" %%btest%d = icmp slt i32 %%bext%d, %d" % (i, i, i + 50))
print(" br i1 %%btest%d, label %%after0, label %%%s" % (i, next))
print("")
print('%s:' % next)
print("%s:" % next)
a, b = 1, 1
for i in range(0, main_size, 6):
a, b = b, a + b
offset = 4096 + b % 500000
value = a % 256
print(' %%ptr%d = getelementptr i8, i8 *%%base, i64 %d' % (i, offset))
print(' store volatile i8 %d, i8 *%%ptr%d' % (value, i))
print(" %%ptr%d = getelementptr i8, i8 *%%base, i64 %d" % (i, offset))
print(" store volatile i8 %d, i8 *%%ptr%d" % (value, i))
for i in range(branch_blocks):
print(' %%acur%d = load i8 , i8 *%%stop' % i)
print(' %%aext%d = sext i8 %%acur%d to i32' % (i, i))
print(' %%atest%d = icmp slt i32 %%aext%d, %d' % (i, i, i + 100))
print(' br i1 %%atest%d, label %%main, label %%after%d' % (i, i))
print('')
print('after%d:' % i)
print(" %%acur%d = load i8 , i8 *%%stop" % i)
print(" %%aext%d = sext i8 %%acur%d to i32" % (i, i))
print(" %%atest%d = icmp slt i32 %%aext%d, %d" % (i, i, i + 100))
print(" br i1 %%atest%d, label %%main, label %%after%d" % (i, i))
print("")
print("after%d:" % i)
print(' %dummy = load volatile i32, i32 *@global')
print(' ret void')
print('}')
print(" %dummy = load volatile i32, i32 *@global")
print(" ret void")
print("}")

View File

@ -74,41 +74,41 @@
from __future__ import print_function
branch_blocks = 8
main_size = 0xffcc
main_size = 0xFFCC
print('@global = global i32 0')
print("@global = global i32 0")
print('define void @f1(i8 *%base, i8 *%stop) {')
print('entry:')
print(' br label %before0')
print('')
print("define void @f1(i8 *%base, i8 *%stop) {")
print("entry:")
print(" br label %before0")
print("")
for i in range(branch_blocks):
next = 'before%d' % (i + 1) if i + 1 < branch_blocks else 'main'
print('before%d:' % i)
print(' %%bcur%d = load i8 , i8 *%%stop' % i)
print(' %%bext%d = sext i8 %%bcur%d to i64' % (i, i))
print(' %%btest%d = icmp slt i64 %%bext%d, %d' % (i, i, i + 50))
print(' br i1 %%btest%d, label %%after0, label %%%s' % (i, next))
print('')
next = "before%d" % (i + 1) if i + 1 < branch_blocks else "main"
print("before%d:" % i)
print(" %%bcur%d = load i8 , i8 *%%stop" % i)
print(" %%bext%d = sext i8 %%bcur%d to i64" % (i, i))
print(" %%btest%d = icmp slt i64 %%bext%d, %d" % (i, i, i + 50))
print(" br i1 %%btest%d, label %%after0, label %%%s" % (i, next))
print("")
print('%s:' % next)
print("%s:" % next)
a, b = 1, 1
for i in range(0, main_size, 6):
a, b = b, a + b
offset = 4096 + b % 500000
value = a % 256
print(' %%ptr%d = getelementptr i8, i8 *%%base, i64 %d' % (i, offset))
print(' store volatile i8 %d, i8 *%%ptr%d' % (value, i))
print(" %%ptr%d = getelementptr i8, i8 *%%base, i64 %d" % (i, offset))
print(" store volatile i8 %d, i8 *%%ptr%d" % (value, i))
for i in range(branch_blocks):
print(' %%acur%d = load i8 , i8 *%%stop' % i)
print(' %%aext%d = sext i8 %%acur%d to i64' % (i, i))
print(' %%atest%d = icmp slt i64 %%aext%d, %d' % (i, i, i + 100))
print(' br i1 %%atest%d, label %%main, label %%after%d' % (i, i))
print('')
print('after%d:' % i)
print(" %%acur%d = load i8 , i8 *%%stop" % i)
print(" %%aext%d = sext i8 %%acur%d to i64" % (i, i))
print(" %%atest%d = icmp slt i64 %%aext%d, %d" % (i, i, i + 100))
print(" br i1 %%atest%d, label %%main, label %%after%d" % (i, i))
print("")
print("after%d:" % i)
print(' %dummy = load volatile i32, i32 *@global')
print(' ret void')
print('}')
print(" %dummy = load volatile i32, i32 *@global")
print(" ret void")
print("}")

View File

@ -35,36 +35,40 @@
from __future__ import print_function
branch_blocks = 8
main_size = 0xffd8
main_size = 0xFFD8
print('define void @f1(i8 *%base, i32 *%counts) {')
print('entry:')
print("define void @f1(i8 *%base, i32 *%counts) {")
print("entry:")
for i in range(branch_blocks - 1, -1, -1):
print(' %%countptr%d = getelementptr i32, i32 *%%counts, i64 %d' % (i, i))
print(' %%initcount%d = load i32 , i32 *%%countptr%d' % (i, i))
print(' br label %%loop%d' % i)
print('loop%d:' % i)
block1 = 'entry' if i == branch_blocks - 1 else 'loop%d' % (i + 1)
block2 = 'loop0' if i == 0 else 'after%d' % (i - 1)
print((' %%count%d = phi i32 [ %%initcount%d, %%%s ],'
' [ %%nextcount%d, %%%s ]' % (i, i, block1, i, block2)))
print(" %%countptr%d = getelementptr i32, i32 *%%counts, i64 %d" % (i, i))
print(" %%initcount%d = load i32 , i32 *%%countptr%d" % (i, i))
print(" br label %%loop%d" % i)
print("loop%d:" % i)
block1 = "entry" if i == branch_blocks - 1 else "loop%d" % (i + 1)
block2 = "loop0" if i == 0 else "after%d" % (i - 1)
print(
(
" %%count%d = phi i32 [ %%initcount%d, %%%s ],"
" [ %%nextcount%d, %%%s ]" % (i, i, block1, i, block2)
)
)
a, b = 1, 1
for i in range(0, main_size, 6):
a, b = b, a + b
offset = 4096 + b % 500000
value = a % 256
print(' %%ptr%d = getelementptr i8, i8 *%%base, i64 %d' % (i, offset))
print(' store volatile i8 %d, i8 *%%ptr%d' % (value, i))
print(" %%ptr%d = getelementptr i8, i8 *%%base, i64 %d" % (i, offset))
print(" store volatile i8 %d, i8 *%%ptr%d" % (value, i))
for i in range(branch_blocks):
print(' %%nextcount%d = add i32 %%count%d, -1' % (i, i))
print(' %%test%d = icmp ne i32 %%nextcount%d, 0' % (i, i))
print(' br i1 %%test%d, label %%loop%d, label %%after%d' % (i, i, i))
print('')
print('after%d:' % i)
print(" %%nextcount%d = add i32 %%count%d, -1" % (i, i))
print(" %%test%d = icmp ne i32 %%nextcount%d, 0" % (i, i))
print(" br i1 %%test%d, label %%loop%d, label %%after%d" % (i, i, i))
print("")
print("after%d:" % i)
print(' ret void')
print('}')
print(" ret void")
print("}")

View File

@ -36,36 +36,40 @@
from __future__ import print_function
branch_blocks = 8
main_size = 0xffd8
main_size = 0xFFD8
print('define void @f1(i8 *%base, i64 *%counts) {')
print('entry:')
print("define void @f1(i8 *%base, i64 *%counts) {")
print("entry:")
for i in range(branch_blocks - 1, -1, -1):
print(' %%countptr%d = getelementptr i64, i64 *%%counts, i64 %d' % (i, i))
print(' %%initcount%d = load i64 , i64 *%%countptr%d' % (i, i))
print(' br label %%loop%d' % i)
print('loop%d:' % i)
block1 = 'entry' if i == branch_blocks - 1 else 'loop%d' % (i + 1)
block2 = 'loop0' if i == 0 else 'after%d' % (i - 1)
print((' %%count%d = phi i64 [ %%initcount%d, %%%s ],'
' [ %%nextcount%d, %%%s ]' % (i, i, block1, i, block2)))
print(" %%countptr%d = getelementptr i64, i64 *%%counts, i64 %d" % (i, i))
print(" %%initcount%d = load i64 , i64 *%%countptr%d" % (i, i))
print(" br label %%loop%d" % i)
print("loop%d:" % i)
block1 = "entry" if i == branch_blocks - 1 else "loop%d" % (i + 1)
block2 = "loop0" if i == 0 else "after%d" % (i - 1)
print(
(
" %%count%d = phi i64 [ %%initcount%d, %%%s ],"
" [ %%nextcount%d, %%%s ]" % (i, i, block1, i, block2)
)
)
a, b = 1, 1
for i in range(0, main_size, 6):
a, b = b, a + b
offset = 4096 + b % 500000
value = a % 256
print(' %%ptr%d = getelementptr i8, i8 *%%base, i64 %d' % (i, offset))
print(' store volatile i8 %d, i8 *%%ptr%d' % (value, i))
print(" %%ptr%d = getelementptr i8, i8 *%%base, i64 %d" % (i, offset))
print(" store volatile i8 %d, i8 *%%ptr%d" % (value, i))
for i in range(branch_blocks):
print(' %%nextcount%d = add i64 %%count%d, -1' % (i, i))
print(' %%test%d = icmp ne i64 %%nextcount%d, 0' % (i, i))
print(' br i1 %%test%d, label %%loop%d, label %%after%d' % (i, i, i))
print('')
print('after%d:' % i)
print(" %%nextcount%d = add i64 %%count%d, -1" % (i, i))
print(" %%test%d = icmp ne i64 %%nextcount%d, 0" % (i, i))
print(" br i1 %%test%d, label %%loop%d, label %%after%d" % (i, i, i))
print("")
print("after%d:" % i)
print(' ret void')
print('}')
print(" ret void")
print("}")

View File

@ -70,43 +70,43 @@
from __future__ import print_function
branch_blocks = 8
main_size = 0xffcc
main_size = 0xFFCC
print('@global = global i32 0')
print("@global = global i32 0")
print('define void @f1(i8 *%base, i8 *%stop, i32 %limit) {')
print('entry:')
print(' br label %before0')
print('')
print("define void @f1(i8 *%base, i8 *%stop, i32 %limit) {")
print("entry:")
print(" br label %before0")
print("")
for i in range(branch_blocks):
next = 'before%d' % (i + 1) if i + 1 < branch_blocks else 'main'
print('before%d:' % i)
print(' %%bstop%d = getelementptr i8, i8 *%%stop, i64 %d' % (i, i))
print(' %%bcur%d = load i8 , i8 *%%bstop%d' % (i, i))
print(' %%bext%d = sext i8 %%bcur%d to i32' % (i, i))
print(' %%btest%d = icmp ult i32 %%limit, %%bext%d' % (i, i))
print(' br i1 %%btest%d, label %%after0, label %%%s' % (i, next))
print('')
next = "before%d" % (i + 1) if i + 1 < branch_blocks else "main"
print("before%d:" % i)
print(" %%bstop%d = getelementptr i8, i8 *%%stop, i64 %d" % (i, i))
print(" %%bcur%d = load i8 , i8 *%%bstop%d" % (i, i))
print(" %%bext%d = sext i8 %%bcur%d to i32" % (i, i))
print(" %%btest%d = icmp ult i32 %%limit, %%bext%d" % (i, i))
print(" br i1 %%btest%d, label %%after0, label %%%s" % (i, next))
print("")
print('%s:' % next)
print("%s:" % next)
a, b = 1, 1
for i in range(0, main_size, 6):
a, b = b, a + b
offset = 4096 + b % 500000
value = a % 256
print(' %%ptr%d = getelementptr i8, i8 *%%base, i64 %d' % (i, offset))
print(' store volatile i8 %d, i8 *%%ptr%d' % (value, i))
print(" %%ptr%d = getelementptr i8, i8 *%%base, i64 %d" % (i, offset))
print(" store volatile i8 %d, i8 *%%ptr%d" % (value, i))
for i in range(branch_blocks):
print(' %%astop%d = getelementptr i8, i8 *%%stop, i64 %d' % (i, i + 25))
print(' %%acur%d = load i8 , i8 *%%astop%d' % (i, i))
print(' %%aext%d = sext i8 %%acur%d to i32' % (i, i))
print(' %%atest%d = icmp ult i32 %%limit, %%aext%d' % (i, i))
print(' br i1 %%atest%d, label %%main, label %%after%d' % (i, i))
print('')
print('after%d:' % i)
print(" %%astop%d = getelementptr i8, i8 *%%stop, i64 %d" % (i, i + 25))
print(" %%acur%d = load i8 , i8 *%%astop%d" % (i, i))
print(" %%aext%d = sext i8 %%acur%d to i32" % (i, i))
print(" %%atest%d = icmp ult i32 %%limit, %%aext%d" % (i, i))
print(" br i1 %%atest%d, label %%main, label %%after%d" % (i, i))
print("")
print("after%d:" % i)
print(' %dummy = load volatile i32, i32 *@global')
print(' ret void')
print('}')
print(" %dummy = load volatile i32, i32 *@global")
print(" ret void")
print("}")

View File

@ -74,43 +74,43 @@
from __future__ import print_function
branch_blocks = 8
main_size = 0xffcc
main_size = 0xFFCC
print('@global = global i32 0')
print("@global = global i32 0")
print('define void @f1(i8 *%base, i8 *%stop, i64 %limit) {')
print('entry:')
print(' br label %before0')
print('')
print("define void @f1(i8 *%base, i8 *%stop, i64 %limit) {")
print("entry:")
print(" br label %before0")
print("")
for i in range(branch_blocks):
next = 'before%d' % (i + 1) if i + 1 < branch_blocks else 'main'
print('before%d:' % i)
print(' %%bstop%d = getelementptr i8, i8 *%%stop, i64 %d' % (i, i))
print(' %%bcur%d = load i8 , i8 *%%bstop%d' % (i, i))
print(' %%bext%d = sext i8 %%bcur%d to i64' % (i, i))
print(' %%btest%d = icmp ult i64 %%limit, %%bext%d' % (i, i))
print(' br i1 %%btest%d, label %%after0, label %%%s' % (i, next))
print('')
next = "before%d" % (i + 1) if i + 1 < branch_blocks else "main"
print("before%d:" % i)
print(" %%bstop%d = getelementptr i8, i8 *%%stop, i64 %d" % (i, i))
print(" %%bcur%d = load i8 , i8 *%%bstop%d" % (i, i))
print(" %%bext%d = sext i8 %%bcur%d to i64" % (i, i))
print(" %%btest%d = icmp ult i64 %%limit, %%bext%d" % (i, i))
print(" br i1 %%btest%d, label %%after0, label %%%s" % (i, next))
print("")
print('%s:' % next)
print("%s:" % next)
a, b = 1, 1
for i in range(0, main_size, 6):
a, b = b, a + b
offset = 4096 + b % 500000
value = a % 256
print(' %%ptr%d = getelementptr i8, i8 *%%base, i64 %d' % (i, offset))
print(' store volatile i8 %d, i8 *%%ptr%d' % (value, i))
print(" %%ptr%d = getelementptr i8, i8 *%%base, i64 %d" % (i, offset))
print(" store volatile i8 %d, i8 *%%ptr%d" % (value, i))
for i in range(branch_blocks):
print(' %%astop%d = getelementptr i8, i8 *%%stop, i64 %d' % (i, i + 25))
print(' %%acur%d = load i8 , i8 *%%astop%d' % (i, i))
print(' %%aext%d = sext i8 %%acur%d to i64' % (i, i))
print(' %%atest%d = icmp ult i64 %%limit, %%aext%d' % (i, i))
print(' br i1 %%atest%d, label %%main, label %%after%d' % (i, i))
print('')
print('after%d:' % i)
print(" %%astop%d = getelementptr i8, i8 *%%stop, i64 %d" % (i, i + 25))
print(" %%acur%d = load i8 , i8 *%%astop%d" % (i, i))
print(" %%aext%d = sext i8 %%acur%d to i64" % (i, i))
print(" %%atest%d = icmp ult i64 %%limit, %%aext%d" % (i, i))
print(" br i1 %%atest%d, label %%main, label %%after%d" % (i, i))
print("")
print("after%d:" % i)
print(' %dummy = load volatile i32, i32 *@global')
print(' ret void')
print('}')
print(" %dummy = load volatile i32, i32 *@global")
print(" ret void")
print("}")

View File

@ -90,43 +90,43 @@
from __future__ import print_function
branch_blocks = 8
main_size = 0xffc6
main_size = 0xFFC6
print('@global = global i32 0')
print("@global = global i32 0")
print('define void @f1(i8 *%base, i32 *%stopa, i32 *%stopb) {')
print('entry:')
print(' br label %before0')
print('')
print("define void @f1(i8 *%base, i32 *%stopa, i32 *%stopb) {")
print("entry:")
print(" br label %before0")
print("")
for i in range(branch_blocks):
next = 'before%d' % (i + 1) if i + 1 < branch_blocks else 'main'
print('before%d:' % i)
print(' %%bcur%da = load i32 , i32 *%%stopa' % i)
print(' %%bcur%db = load i32 , i32 *%%stopb' % i)
print(' %%bsub%d = sub i32 %%bcur%da, %%bcur%db' % (i, i, i))
print(' %%btest%d = icmp ult i32 %%bsub%d, %d' % (i, i, i + 50))
print(' br i1 %%btest%d, label %%after0, label %%%s' % (i, next))
print('')
next = "before%d" % (i + 1) if i + 1 < branch_blocks else "main"
print("before%d:" % i)
print(" %%bcur%da = load i32 , i32 *%%stopa" % i)
print(" %%bcur%db = load i32 , i32 *%%stopb" % i)
print(" %%bsub%d = sub i32 %%bcur%da, %%bcur%db" % (i, i, i))
print(" %%btest%d = icmp ult i32 %%bsub%d, %d" % (i, i, i + 50))
print(" br i1 %%btest%d, label %%after0, label %%%s" % (i, next))
print("")
print('%s:' % next)
print("%s:" % next)
a, b = 1, 1
for i in range(0, main_size, 6):
a, b = b, a + b
offset = 4096 + b % 500000
value = a % 256
print(' %%ptr%d = getelementptr i8, i8 *%%base, i64 %d' % (i, offset))
print(' store volatile i8 %d, i8 *%%ptr%d' % (value, i))
print(" %%ptr%d = getelementptr i8, i8 *%%base, i64 %d" % (i, offset))
print(" store volatile i8 %d, i8 *%%ptr%d" % (value, i))
for i in range(branch_blocks):
print(' %%acur%da = load i32 , i32 *%%stopa' % i)
print(' %%acur%db = load i32 , i32 *%%stopb' % i)
print(' %%asub%d = sub i32 %%acur%da, %%acur%db' % (i, i, i))
print(' %%atest%d = icmp ult i32 %%asub%d, %d' % (i, i, i + 100))
print(' br i1 %%atest%d, label %%main, label %%after%d' % (i, i))
print('')
print('after%d:' % i)
print(" %%acur%da = load i32 , i32 *%%stopa" % i)
print(" %%acur%db = load i32 , i32 *%%stopb" % i)
print(" %%asub%d = sub i32 %%acur%da, %%acur%db" % (i, i, i))
print(" %%atest%d = icmp ult i32 %%asub%d, %d" % (i, i, i + 100))
print(" br i1 %%atest%d, label %%main, label %%after%d" % (i, i))
print("")
print("after%d:" % i)
print(' %dummy = load volatile i32, i32 *@global')
print(' ret void')
print('}')
print(" %dummy = load volatile i32, i32 *@global")
print(" ret void")
print("}")

View File

@ -90,43 +90,43 @@
from __future__ import print_function
branch_blocks = 8
main_size = 0xffb4
main_size = 0xFFB4
print('@global = global i32 0')
print("@global = global i32 0")
print('define void @f1(i8 *%base, i64 *%stopa, i64 *%stopb) {')
print('entry:')
print(' br label %before0')
print('')
print("define void @f1(i8 *%base, i64 *%stopa, i64 *%stopb) {")
print("entry:")
print(" br label %before0")
print("")
for i in range(branch_blocks):
next = 'before%d' % (i + 1) if i + 1 < branch_blocks else 'main'
print('before%d:' % i)
print(' %%bcur%da = load i64 , i64 *%%stopa' % i)
print(' %%bcur%db = load i64 , i64 *%%stopb' % i)
print(' %%bsub%d = sub i64 %%bcur%da, %%bcur%db' % (i, i, i))
print(' %%btest%d = icmp ult i64 %%bsub%d, %d' % (i, i, i + 50))
print(' br i1 %%btest%d, label %%after0, label %%%s' % (i, next))
print('')
next = "before%d" % (i + 1) if i + 1 < branch_blocks else "main"
print("before%d:" % i)
print(" %%bcur%da = load i64 , i64 *%%stopa" % i)
print(" %%bcur%db = load i64 , i64 *%%stopb" % i)
print(" %%bsub%d = sub i64 %%bcur%da, %%bcur%db" % (i, i, i))
print(" %%btest%d = icmp ult i64 %%bsub%d, %d" % (i, i, i + 50))
print(" br i1 %%btest%d, label %%after0, label %%%s" % (i, next))
print("")
print('%s:' % next)
print("%s:" % next)
a, b = 1, 1
for i in range(0, main_size, 6):
a, b = b, a + b
offset = 4096 + b % 500000
value = a % 256
print(' %%ptr%d = getelementptr i8, i8 *%%base, i64 %d' % (i, offset))
print(' store volatile i8 %d, i8 *%%ptr%d' % (value, i))
print(" %%ptr%d = getelementptr i8, i8 *%%base, i64 %d" % (i, offset))
print(" store volatile i8 %d, i8 *%%ptr%d" % (value, i))
for i in range(branch_blocks):
print(' %%acur%da = load i64 , i64 *%%stopa' % i)
print(' %%acur%db = load i64 , i64 *%%stopb' % i)
print(' %%asub%d = sub i64 %%acur%da, %%acur%db' % (i, i, i))
print(' %%atest%d = icmp ult i64 %%asub%d, %d' % (i, i, i + 100))
print(' br i1 %%atest%d, label %%main, label %%after%d' % (i, i))
print('')
print('after%d:' % i)
print(" %%acur%da = load i64 , i64 *%%stopa" % i)
print(" %%acur%db = load i64 , i64 *%%stopb" % i)
print(" %%asub%d = sub i64 %%acur%da, %%acur%db" % (i, i, i))
print(" %%atest%d = icmp ult i64 %%asub%d, %d" % (i, i, i + 100))
print(" br i1 %%atest%d, label %%main, label %%after%d" % (i, i))
print("")
print("after%d:" % i)
print(' %dummy = load volatile i32, i32 *@global')
print(' ret void')
print('}')
print(" %dummy = load volatile i32, i32 *@global")
print(" ret void")
print("}")

View File

@ -24,18 +24,20 @@ from __future__ import print_function
num = 11000
print('define void @f1() {')
print('entry:')
print(' br label %block')
print('')
print('block:')
print("define void @f1() {")
print("entry:")
print(" br label %block")
print("")
print("block:")
for i in range(num):
print(' tail call i64 asm "lang\\09$0,$2,$1\\0A", "=d,=*Q,d,*Q"(i32* elementtype(i32) undef, i32 undef, i32* elementtype(i32) undef)')
print(
' tail call i64 asm "lang\\09$0,$2,$1\\0A", "=d,=*Q,d,*Q"(i32* elementtype(i32) undef, i32 undef, i32* elementtype(i32) undef)'
)
print(' br label %block')
print(" br label %block")
print('')
print('exit:')
print(' ret void')
print('}')
print("")
print("exit:")
print(" ret void")
print("}")

View File

@ -23,21 +23,21 @@ from __future__ import print_function
count = 500
print('declare void @foo()')
print('')
print('define void @f1(i64 *%base0, i64 *%base1) {')
print("declare void @foo()")
print("")
print("define void @f1(i64 *%base0, i64 *%base1) {")
for i in range(count):
print(' %%ptr%d = getelementptr i64, i64 *%%base%d, i64 %d' % (i, i % 2, i / 2))
print(' %%val%d = load i64 , i64 *%%ptr%d' % (i, i))
print('')
print(" %%ptr%d = getelementptr i64, i64 *%%base%d, i64 %d" % (i, i % 2, i / 2))
print(" %%val%d = load i64 , i64 *%%ptr%d" % (i, i))
print("")
print(' call void @foo()')
print('')
print(" call void @foo()")
print("")
for i in range(count):
print(' store i64 %%val%d, i64 *%%ptr%d' % (i, i))
print(" store i64 %%val%d, i64 *%%ptr%d" % (i, i))
print('')
print(' ret void')
print('}')
print("")
print(" ret void")
print("}")

View File

@ -24,53 +24,53 @@ from __future__ import print_function
args = int((8168 - 160) / 8 + (5 - 1))
print('declare i64 *@foo(i64 *%s)' % (', i64' * args))
print('declare void @bar(i64 *)')
print('')
print('define i64 @f1(i64 %foo) {')
print('entry:')
print("declare i64 *@foo(i64 *%s)" % (", i64" * args))
print("declare void @bar(i64 *)")
print("")
print("define i64 @f1(i64 %foo) {")
print("entry:")
# Make the allocation big, so that it goes at the top of the frame.
print(' %array = alloca [1000 x i64]')
print(' %area = getelementptr [1000 x i64], [1000 x i64] *%array, i64 0, i64 0')
print(' %%base = call i64 *@foo(i64 *%%area%s)' % (', i64 0' * args))
print('')
print(" %array = alloca [1000 x i64]")
print(" %area = getelementptr [1000 x i64], [1000 x i64] *%array, i64 0, i64 0")
print(" %%base = call i64 *@foo(i64 *%%area%s)" % (", i64 0" * args))
print("")
# Make sure all GPRs are used. One is needed for the stack pointer and
# another for %base, so we need 14 live values.
count = 14
for i in range(count):
print(' %%ptr%d = getelementptr i64, i64 *%%base, i64 %d' % (i, i / 2))
print(' %%val%d = load volatile i64 , i64 *%%ptr%d' % (i, i))
print('')
print(" %%ptr%d = getelementptr i64, i64 *%%base, i64 %d" % (i, i / 2))
print(" %%val%d = load volatile i64 , i64 *%%ptr%d" % (i, i))
print("")
# Encourage the register allocator to give preference to these %vals
# by using them several times.
for j in range(4):
for i in range(count):
print(' store volatile i64 %%val%d, i64 *%%ptr%d' % (i, i))
print('')
print(" store volatile i64 %%val%d, i64 *%%ptr%d" % (i, i))
print("")
# Copy the incoming argument, which we expect to be spilled, to the frame
# index for the alloca area. Also throw in a volatile store, so that this
# block cannot be reordered with the surrounding code.
print(' %cond = icmp eq i64 %val0, %val1')
print(' br i1 %cond, label %skip, label %fallthru')
print('')
print('fallthru:')
print(' store i64 %foo, i64 *%area')
print(' store volatile i64 %val0, i64 *%ptr0')
print(' br label %skip')
print('')
print('skip:')
print(" %cond = icmp eq i64 %val0, %val1")
print(" br i1 %cond, label %skip, label %fallthru")
print("")
print("fallthru:")
print(" store i64 %foo, i64 *%area")
print(" store volatile i64 %val0, i64 *%ptr0")
print(" br label %skip")
print("")
print("skip:")
# Use each %val a few more times to emphasise the point, and to make sure
# that they are live across the store of %foo.
for j in range(4):
for i in range(count):
print(' store volatile i64 %%val%d, i64 *%%ptr%d' % (i, i))
print('')
print(" store volatile i64 %%val%d, i64 *%%ptr%d" % (i, i))
print("")
print(' call void @bar(i64 *%area)')
print(' ret i64 0')
print('}')
print(" call void @bar(i64 *%area)")
print(" ret i64 0")
print("}")

View File

@ -36,183 +36,182 @@ MAX_OP_USES = 2
def get_num_defs(program):
num_defs = 0
for _, defs in program:
num_defs += len(defs)
return num_defs
num_defs = 0
for _, defs in program:
num_defs += len(defs)
return num_defs
def possible_ops(program):
program_defs = get_num_defs(program)
for num_defs in range(MAX_PROGRAM_DEFS - program_defs + 1):
for num_uses in range(MAX_OP_USES + 1):
if num_defs == 0 and num_uses == 0:
continue
for uses in product(range(program_defs), repeat=num_uses):
yield uses, tuple(program_defs + i for i in range(num_defs))
program_defs = get_num_defs(program)
for num_defs in range(MAX_PROGRAM_DEFS - program_defs + 1):
for num_uses in range(MAX_OP_USES + 1):
if num_defs == 0 and num_uses == 0:
continue
for uses in product(range(program_defs), repeat=num_uses):
yield uses, tuple(program_defs + i for i in range(num_defs))
def generate_programs():
queue = deque()
queue.append([])
program_id = 0
while True:
program = queue.popleft()
if len(program) == MAX_PROGRAM_OPS:
break
for op in possible_ops(program):
program_id += 1
new_program = program + [op]
queue.append(new_program)
yield program_id, new_program
queue = deque()
queue.append([])
program_id = 0
while True:
program = queue.popleft()
if len(program) == MAX_PROGRAM_OPS:
break
for op in possible_ops(program):
program_id += 1
new_program = program + [op]
queue.append(new_program)
yield program_id, new_program
def get_num_terminal_ops(program):
num_terminal_ops = 0
for _, defs in program:
if len(defs) == 0:
num_terminal_ops += 1
return num_terminal_ops
num_terminal_ops = 0
for _, defs in program:
if len(defs) == 0:
num_terminal_ops += 1
return num_terminal_ops
def get_max_uses(program):
num_uses = [0] * MAX_PROGRAM_DEFS
for uses, _ in program:
for u in uses:
num_uses[u] += 1
return max(num_uses)
num_uses = [0] * MAX_PROGRAM_DEFS
for uses, _ in program:
for u in uses:
num_uses[u] += 1
return max(num_uses)
def has_unused_op(program):
used = [False] * MAX_PROGRAM_DEFS
for uses, defs in program[::-1]:
if defs and all(not used[d] for d in defs):
return True
for u in uses:
used[u] = True
return False
used = [False] * MAX_PROGRAM_DEFS
for uses, defs in program[::-1]:
if defs and all(not used[d] for d in defs):
return True
for u in uses:
used[u] = True
return False
def has_multivalue_use(program):
is_multi = [False] * MAX_PROGRAM_DEFS
for uses, defs in program:
if any(is_multi[u] for u in uses):
return True
if len(defs) >= 2:
for d in defs:
is_multi[d] = True
return False
is_multi = [False] * MAX_PROGRAM_DEFS
for uses, defs in program:
if any(is_multi[u] for u in uses):
return True
if len(defs) >= 2:
for d in defs:
is_multi[d] = True
return False
def has_mvp_use(program):
is_mvp = [False] * MAX_PROGRAM_DEFS
for uses, defs in program:
if uses and all(is_mvp[u] for u in uses):
return True
if len(defs) <= 1:
if any(is_mvp[u] for u in uses):
return True
for d in defs:
is_mvp[d] = True
return False
is_mvp = [False] * MAX_PROGRAM_DEFS
for uses, defs in program:
if uses and all(is_mvp[u] for u in uses):
return True
if len(defs) <= 1:
if any(is_mvp[u] for u in uses):
return True
for d in defs:
is_mvp[d] = True
return False
def is_interesting(program):
# Allow only multivalue single-op programs
if len(program) == 1:
return len(program[0][1]) > 1
# Allow only multivalue single-op programs
if len(program) == 1:
return len(program[0][1]) > 1
# Reject programs where the last two instructions are identical
if len(program) >= 2 and program[-1][0] == program[-2][0]:
return False
# Reject programs where the last two instructions are identical
if len(program) >= 2 and program[-1][0] == program[-2][0]:
return False
# Reject programs with too many ops that don't produce values
if get_num_terminal_ops(program) > 2:
return False
# Reject programs with too many ops that don't produce values
if get_num_terminal_ops(program) > 2:
return False
# The third use of a value is no more interesting than the second
if get_max_uses(program) >= 3:
return False
# The third use of a value is no more interesting than the second
if get_max_uses(program) >= 3:
return False
# Reject nontrivial programs that have unused instructions
if has_unused_op(program):
return False
# Reject nontrivial programs that have unused instructions
if has_unused_op(program):
return False
# Reject programs that have boring MVP uses of MVP defs
if has_mvp_use(program):
return False
# Reject programs that have boring MVP uses of MVP defs
if has_mvp_use(program):
return False
# Otherwise if it has multivalue usage it is interesting
return has_multivalue_use(program)
# Otherwise if it has multivalue usage it is interesting
return has_multivalue_use(program)
def make_llvm_type(num_defs):
if num_defs == 0:
return 'void'
else:
return '{' + ', '.join(['i32'] * num_defs) + '}'
if num_defs == 0:
return "void"
else:
return "{" + ", ".join(["i32"] * num_defs) + "}"
def make_llvm_op_name(num_uses, num_defs):
return f'op_{num_uses}_to_{num_defs}'
return f"op_{num_uses}_to_{num_defs}"
def make_llvm_args(first_use, num_uses):
return ', '.join([f'i32 %t{first_use + i}' for i in range(num_uses)])
return ", ".join([f"i32 %t{first_use + i}" for i in range(num_uses)])
def print_llvm_program(program, name):
tmp = 0
def_data = []
print(f'define void @{name}() {{')
for uses, defs in program:
first_arg = tmp
# Extract operands
for use in uses:
ret_type, var, idx = def_data[use]
print(f' %t{tmp} = extractvalue {ret_type} %t{var}, {idx}')
tmp += 1
# Print instruction
assignment = ''
if len(defs) > 0:
assignment = f'%t{tmp} = '
result_var = tmp
tmp += 1
ret_type = make_llvm_type(len(defs))
op_name = make_llvm_op_name(len(uses), len(defs))
args = make_llvm_args(first_arg, len(uses))
print(f' {assignment}call {ret_type} @{op_name}({args})')
# Update def_data
for i in range(len(defs)):
def_data.append((ret_type, result_var, i))
print(' ret void')
print('}')
tmp = 0
def_data = []
print(f"define void @{name}() {{")
for uses, defs in program:
first_arg = tmp
# Extract operands
for use in uses:
ret_type, var, idx = def_data[use]
print(f" %t{tmp} = extractvalue {ret_type} %t{var}, {idx}")
tmp += 1
# Print instruction
assignment = ""
if len(defs) > 0:
assignment = f"%t{tmp} = "
result_var = tmp
tmp += 1
ret_type = make_llvm_type(len(defs))
op_name = make_llvm_op_name(len(uses), len(defs))
args = make_llvm_args(first_arg, len(uses))
print(f" {assignment}call {ret_type} @{op_name}({args})")
# Update def_data
for i in range(len(defs)):
def_data.append((ret_type, result_var, i))
print(" ret void")
print("}")
def print_header():
print('; NOTE: Test functions have been generated by multivalue-stackify.py.')
print()
print('; RUN: llc < %s -verify-machineinstrs -mattr=+multivalue',
'| FileCheck %s')
print()
print('; Test that the multivalue stackification works')
print()
print('target triple = "wasm32-unknown-unknown"')
print()
for num_uses in range(MAX_OP_USES + 1):
for num_defs in range(MAX_PROGRAM_DEFS + 1):
if num_uses == 0 and num_defs == 0:
continue
ret_type = make_llvm_type(num_defs)
op_name = make_llvm_op_name(num_uses, num_defs)
args = make_llvm_args(0, num_uses)
print(f'declare {ret_type} @{op_name}({args})')
print()
print("; NOTE: Test functions have been generated by multivalue-stackify.py.")
print()
print("; RUN: llc < %s -verify-machineinstrs -mattr=+multivalue", "| FileCheck %s")
print()
print("; Test that the multivalue stackification works")
print()
print('target triple = "wasm32-unknown-unknown"')
print()
for num_uses in range(MAX_OP_USES + 1):
for num_defs in range(MAX_PROGRAM_DEFS + 1):
if num_uses == 0 and num_defs == 0:
continue
ret_type = make_llvm_type(num_defs)
op_name = make_llvm_op_name(num_uses, num_defs)
args = make_llvm_args(0, num_uses)
print(f"declare {ret_type} @{op_name}({args})")
print()
if __name__ == '__main__':
print_header()
for i, program in generate_programs():
if is_interesting(program):
print_llvm_program(program, 'f' + str(i))
print()
if __name__ == "__main__":
print_header()
for i, program in generate_programs():
if is_interesting(program):
print_llvm_program(program, "f" + str(i))
print()

View File

@ -22,8 +22,11 @@ num_sections = 65277
# CHECK-NEXT: }
for i in range(0, num_sections):
print(""" .section .bss,"bw",discard,_b%d
print(
""" .section .bss,"bw",discard,_b%d
.globl _b%d # @b%d
_b%d:
.byte 0 # 0x0
""" % (i, i, i, i))
"""
% (i, i, i, i)
)

View File

@ -9,12 +9,12 @@ import subprocess
parser = argparse.ArgumentParser()
parser.add_argument('--start', type=int, default=0)
parser.add_argument('--end', type=int, default=(1 << 32))
parser.add_argument('--optcmd', default=("opt"))
parser.add_argument('--filecheckcmd', default=("FileCheck"))
parser.add_argument('--prefix', default=("CHECK-BISECT"))
parser.add_argument('--test', default=(""))
parser.add_argument("--start", type=int, default=0)
parser.add_argument("--end", type=int, default=(1 << 32))
parser.add_argument("--optcmd", default=("opt"))
parser.add_argument("--filecheckcmd", default=("FileCheck"))
parser.add_argument("--prefix", default=("CHECK-BISECT"))
parser.add_argument("--test", default=(""))
args = parser.parse_args()
@ -24,9 +24,9 @@ end = args.end
opt_command = [args.optcmd, "-O2", "-opt-bisect-limit=%(count)s", "-S", args.test]
check_command = [args.filecheckcmd, args.test, "--check-prefix=%s" % args.prefix]
last = None
while start != end and start != end-1:
count = int(round(start + (end - start)/2))
cmd = [x % {'count':count} for x in opt_command]
while start != end and start != end - 1:
count = int(round(start + (end - start) / 2))
cmd = [x % {"count": count} for x in opt_command]
print("opt: " + str(cmd))
opt_result = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
filecheck_result = subprocess.Popen(check_command, stdin=opt_result.stdout)

View File

@ -21,11 +21,11 @@ with open(testfile) as testfh:
prefix_pos = line.index(prefix)
except ValueError:
continue
check_expr = line[prefix_pos + len(prefix):]
check_expr = line[prefix_pos + len(prefix) :]
try:
exception = None
result = eval(check_expr, {"data":data})
result = eval(check_expr, {"data": data})
except Exception:
result = False
exception = traceback.format_exc().splitlines()[-1]
@ -34,13 +34,16 @@ with open(testfile) as testfh:
sys.stderr.write(
"{file}:{line:d}: check threw exception: {expr}\n"
"{file}:{line:d}: exception was: {exception}\n".format(
file=testfile, line=lineno,
expr=check_expr, exception=exception))
file=testfile, line=lineno, expr=check_expr, exception=exception
)
)
fails += 1
elif not result:
sys.stderr.write(
"{file}:{line:d}: check returned False: {expr}\n".format(
file=testfile, line=lineno, expr=check_expr))
file=testfile, line=lineno, expr=check_expr
)
)
fails += 1
else:
passes += 1

View File

@ -3,19 +3,18 @@ import sys
def main(args):
class Advisor:
to_return = False
class Advisor:
to_return = False
def advice(self, _):
# The adice will be a sequence of yes/no/yes/no/...
# see ../interactive-mode.ll
self.to_return = not self.to_return
return int(self.to_return)
def advice(self, _):
# The adice will be a sequence of yes/no/yes/no/...
# see ../interactive-mode.ll
self.to_return = not self.to_return
return int(self.to_return)
a = Advisor()
interactive_host.run_interactive(args[0], a.advice, args[1:])
a = Advisor()
interactive_host.run_interactive(args[0], a.advice, args[1:])
if __name__ == '__main__':
main(sys.argv[1:])
if __name__ == "__main__":
main(sys.argv[1:])

View File

@ -8,52 +8,53 @@ import subprocess
import lit.formats
# name: The name of this test suite.
config.name = 'LLVM-Unit'
config.name = "LLVM-Unit"
# suffixes: A list of file extensions to treat as test files.
config.suffixes = []
# test_source_root: The root path where tests are located.
# test_exec_root: The root path where tests should be run.
config.test_exec_root = os.path.join(config.llvm_obj_root, 'unittests')
config.test_exec_root = os.path.join(config.llvm_obj_root, "unittests")
config.test_source_root = config.test_exec_root
# testFormat: The test format to use to interpret tests.
config.test_format = lit.formats.GoogleTest(config.llvm_build_mode, 'Tests')
config.test_format = lit.formats.GoogleTest(config.llvm_build_mode, "Tests")
# Propagate the temp directory. Windows requires this because it uses \Windows\
# if none of these are present.
if 'TMP' in os.environ:
config.environment['TMP'] = os.environ['TMP']
if 'TEMP' in os.environ:
config.environment['TEMP'] = os.environ['TEMP']
if "TMP" in os.environ:
config.environment["TMP"] = os.environ["TMP"]
if "TEMP" in os.environ:
config.environment["TEMP"] = os.environ["TEMP"]
# Propagate HOME as it can be used to override incorrect homedir in passwd
# that causes the tests to fail.
if 'HOME' in os.environ:
config.environment['HOME'] = os.environ['HOME']
if "HOME" in os.environ:
config.environment["HOME"] = os.environ["HOME"]
# Propagate sanitizer options.
for var in [
'ASAN_SYMBOLIZER_PATH',
'HWASAN_SYMBOLIZER_PATH',
'MSAN_SYMBOLIZER_PATH',
'TSAN_SYMBOLIZER_PATH',
'UBSAN_SYMBOLIZER_PATH',
'ASAN_OPTIONS',
'HWASAN_OPTIONS',
'MSAN_OPTIONS',
'TSAN_OPTIONS',
'UBSAN_OPTIONS',
"ASAN_SYMBOLIZER_PATH",
"HWASAN_SYMBOLIZER_PATH",
"MSAN_SYMBOLIZER_PATH",
"TSAN_SYMBOLIZER_PATH",
"UBSAN_SYMBOLIZER_PATH",
"ASAN_OPTIONS",
"HWASAN_OPTIONS",
"MSAN_OPTIONS",
"TSAN_OPTIONS",
"UBSAN_OPTIONS",
]:
if var in os.environ:
config.environment[var] = os.environ[var]
# Win32 seeks DLLs along %PATH%.
if sys.platform in ['win32', 'cygwin'] and os.path.isdir(config.shlibdir):
config.environment['PATH'] = os.path.pathsep.join((
config.shlibdir, config.environment['PATH']))
if sys.platform in ["win32", "cygwin"] and os.path.isdir(config.shlibdir):
config.environment["PATH"] = os.path.pathsep.join(
(config.shlibdir, config.environment["PATH"])
)
# Win32 may use %SYSTEMDRIVE% during file system shell operations, so propogate.
if sys.platform == 'win32' and 'SYSTEMDRIVE' in os.environ:
config.environment['SYSTEMDRIVE'] = os.environ['SYSTEMDRIVE']
if sys.platform == "win32" and "SYSTEMDRIVE" in os.environ:
config.environment["SYSTEMDRIVE"] = os.environ["SYSTEMDRIVE"]

View File

@ -15,81 +15,82 @@ from lit.llvm.subst import FindTool
from lit.llvm.subst import ToolSubst
# name: The name of this test suite.
config.name = 'LLVM'
config.name = "LLVM"
# testFormat: The test format to use to interpret tests.
config.test_format = lit.formats.ShTest(not llvm_config.use_lit_shell)
# suffixes: A list of file extensions to treat as test files. This is overriden
# by individual lit.local.cfg files in the test subdirectories.
config.suffixes = ['.ll', '.c', '.test', '.txt', '.s', '.mir', '.yaml']
config.suffixes = [".ll", ".c", ".test", ".txt", ".s", ".mir", ".yaml"]
# excludes: A list of directories to exclude from the testsuite. The 'Inputs'
# subdirectories contain auxiliary inputs for various tests in their parent
# directories.
config.excludes = ['Inputs', 'CMakeLists.txt', 'README.txt', 'LICENSE.txt']
config.excludes = ["Inputs", "CMakeLists.txt", "README.txt", "LICENSE.txt"]
# test_source_root: The root path where tests are located.
config.test_source_root = os.path.dirname(__file__)
# test_exec_root: The root path where tests should be run.
config.test_exec_root = os.path.join(config.llvm_obj_root, 'test')
config.test_exec_root = os.path.join(config.llvm_obj_root, "test")
# Tweak the PATH to include the tools dir.
llvm_config.with_environment('PATH', config.llvm_tools_dir, append_path=True)
llvm_config.with_environment("PATH", config.llvm_tools_dir, append_path=True)
# Propagate some variables from the host environment.
llvm_config.with_system_environment(
['HOME', 'INCLUDE', 'LIB', 'TMP', 'TEMP'])
llvm_config.with_system_environment(["HOME", "INCLUDE", "LIB", "TMP", "TEMP"])
# Set up OCAMLPATH to include newly built OCaml libraries.
top_ocaml_lib = os.path.join(config.llvm_lib_dir, 'ocaml')
llvm_ocaml_lib = os.path.join(top_ocaml_lib, 'llvm')
top_ocaml_lib = os.path.join(config.llvm_lib_dir, "ocaml")
llvm_ocaml_lib = os.path.join(top_ocaml_lib, "llvm")
llvm_config.with_system_environment('OCAMLPATH')
llvm_config.with_environment('OCAMLPATH', top_ocaml_lib, append_path=True)
llvm_config.with_environment('OCAMLPATH', llvm_ocaml_lib, append_path=True)
llvm_config.with_system_environment("OCAMLPATH")
llvm_config.with_environment("OCAMLPATH", top_ocaml_lib, append_path=True)
llvm_config.with_environment("OCAMLPATH", llvm_ocaml_lib, append_path=True)
llvm_config.with_system_environment('CAML_LD_LIBRARY_PATH')
llvm_config.with_environment(
'CAML_LD_LIBRARY_PATH', llvm_ocaml_lib, append_path=True)
llvm_config.with_system_environment("CAML_LD_LIBRARY_PATH")
llvm_config.with_environment("CAML_LD_LIBRARY_PATH", llvm_ocaml_lib, append_path=True)
# Set up OCAMLRUNPARAM to enable backtraces in OCaml tests.
llvm_config.with_environment('OCAMLRUNPARAM', 'b')
llvm_config.with_environment("OCAMLRUNPARAM", "b")
# Provide the path to asan runtime lib 'libclang_rt.asan_osx_dynamic.dylib' if
# available. This is darwin specific since it's currently only needed on darwin.
def get_asan_rtlib():
if not 'Address' in config.llvm_use_sanitizer or \
not 'Darwin' in config.host_os or \
not 'x86' in config.host_triple:
return ''
if (
not "Address" in config.llvm_use_sanitizer
or not "Darwin" in config.host_os
or not "x86" in config.host_triple
):
return ""
try:
import glob
except:
print('glob module not found, skipping get_asan_rtlib() lookup')
return ''
print("glob module not found, skipping get_asan_rtlib() lookup")
return ""
# The libclang_rt.asan_osx_dynamic.dylib path is obtained using the relative
# path from the host cc.
host_lib_dir = os.path.join(os.path.dirname(config.host_cc), '../lib')
asan_dylib_dir_pattern = host_lib_dir + \
'/clang/*/lib/darwin/libclang_rt.asan_osx_dynamic.dylib'
host_lib_dir = os.path.join(os.path.dirname(config.host_cc), "../lib")
asan_dylib_dir_pattern = (
host_lib_dir + "/clang/*/lib/darwin/libclang_rt.asan_osx_dynamic.dylib"
)
found_dylibs = glob.glob(asan_dylib_dir_pattern)
if len(found_dylibs) != 1:
return ''
return ""
return found_dylibs[0]
llvm_config.use_default_substitutions()
# Add site-specific substitutions.
config.substitutions.append(('%llvmshlibdir', config.llvm_shlib_dir))
config.substitutions.append(('%shlibext', config.llvm_shlib_ext))
config.substitutions.append(('%pluginext', config.llvm_plugin_ext))
config.substitutions.append(('%exeext', config.llvm_exe_ext))
config.substitutions.append(("%llvmshlibdir", config.llvm_shlib_dir))
config.substitutions.append(("%shlibext", config.llvm_shlib_ext))
config.substitutions.append(("%pluginext", config.llvm_plugin_ext))
config.substitutions.append(("%exeext", config.llvm_exe_ext))
lli_args = []
@ -98,15 +99,14 @@ lli_args = []
# we don't support COFF in MCJIT well enough for the tests, force ELF format on
# Windows. FIXME: the process target triple should be used here, but this is
# difficult to obtain on Windows.
if re.search(r'cygwin|windows-gnu|windows-msvc', config.host_triple):
lli_args = ['-mtriple=' + config.host_triple + '-elf']
if re.search(r"cygwin|windows-gnu|windows-msvc", config.host_triple):
lli_args = ["-mtriple=" + config.host_triple + "-elf"]
llc_args = []
# Similarly, have a macro to use llc with DWARF even when the host is Windows
if re.search(r'windows-msvc', config.target_triple):
llc_args = [' -mtriple=' +
config.target_triple.replace('-msvc', '-gnu')]
if re.search(r"windows-msvc", config.target_triple):
llc_args = [" -mtriple=" + config.target_triple.replace("-msvc", "-gnu")]
# Provide the path to asan runtime lib if available. On darwin, this lib needs
# to be loaded via DYLD_INSERT_LIBRARIES before libLTO.dylib in case the files
@ -114,95 +114,171 @@ if re.search(r'windows-msvc', config.target_triple):
ld64_cmd = config.ld64_executable
asan_rtlib = get_asan_rtlib()
if asan_rtlib:
ld64_cmd = 'DYLD_INSERT_LIBRARIES={} {}'.format(asan_rtlib, ld64_cmd)
ld64_cmd = "DYLD_INSERT_LIBRARIES={} {}".format(asan_rtlib, ld64_cmd)
if config.osx_sysroot:
ld64_cmd = '{} -syslibroot {}'.format(ld64_cmd, config.osx_sysroot)
ld64_cmd = "{} -syslibroot {}".format(ld64_cmd, config.osx_sysroot)
ocamlc_command = '%s ocamlc -cclib -L%s %s' % (
config.ocamlfind_executable, config.llvm_lib_dir, config.ocaml_flags)
ocamlopt_command = 'true'
ocamlc_command = "%s ocamlc -cclib -L%s %s" % (
config.ocamlfind_executable,
config.llvm_lib_dir,
config.ocaml_flags,
)
ocamlopt_command = "true"
if config.have_ocamlopt:
ocamlopt_command = '%s ocamlopt -cclib -L%s -cclib -Wl,-rpath,%s %s' % (
config.ocamlfind_executable, config.llvm_lib_dir, config.llvm_lib_dir, config.ocaml_flags)
ocamlopt_command = "%s ocamlopt -cclib -L%s -cclib -Wl,-rpath,%s %s" % (
config.ocamlfind_executable,
config.llvm_lib_dir,
config.llvm_lib_dir,
config.ocaml_flags,
)
opt_viewer_cmd = '%s %s/tools/opt-viewer/opt-viewer.py' % (sys.executable, config.llvm_src_root)
opt_viewer_cmd = "%s %s/tools/opt-viewer/opt-viewer.py" % (
sys.executable,
config.llvm_src_root,
)
llvm_original_di_preservation_cmd = os.path.join(
config.llvm_src_root,'utils', 'llvm-original-di-preservation.py')
config.llvm_src_root, "utils", "llvm-original-di-preservation.py"
)
config.substitutions.append(
('%llvm-original-di-preservation', "'%s' %s" % (
config.python_executable, llvm_original_di_preservation_cmd)))
(
"%llvm-original-di-preservation",
"'%s' %s" % (config.python_executable, llvm_original_di_preservation_cmd),
)
)
llvm_locstats_tool = os.path.join(config.llvm_tools_dir, 'llvm-locstats')
llvm_locstats_tool = os.path.join(config.llvm_tools_dir, "llvm-locstats")
config.substitutions.append(
('%llvm-locstats', "'%s' %s" % (config.python_executable, llvm_locstats_tool)))
("%llvm-locstats", "'%s' %s" % (config.python_executable, llvm_locstats_tool))
)
config.llvm_locstats_used = os.path.exists(llvm_locstats_tool)
tools = [
ToolSubst('%llvm', FindTool('llvm'), unresolved='ignore'),
ToolSubst('%lli', FindTool('lli'), post='.', extra_args=lli_args),
ToolSubst('%llc_dwarf', FindTool('llc'), extra_args=llc_args),
ToolSubst('%gold', config.gold_executable, unresolved='ignore'),
ToolSubst('%ld64', ld64_cmd, unresolved='ignore'),
ToolSubst('%ocamlc', ocamlc_command, unresolved='ignore'),
ToolSubst('%ocamlopt', ocamlopt_command, unresolved='ignore'),
ToolSubst('%opt-viewer', opt_viewer_cmd),
ToolSubst('%llvm-objcopy', FindTool('llvm-objcopy')),
ToolSubst('%llvm-strip', FindTool('llvm-strip')),
ToolSubst('%llvm-install-name-tool', FindTool('llvm-install-name-tool')),
ToolSubst('%llvm-bitcode-strip', FindTool('llvm-bitcode-strip')),
ToolSubst('%split-file', FindTool('split-file')),
ToolSubst("%llvm", FindTool("llvm"), unresolved="ignore"),
ToolSubst("%lli", FindTool("lli"), post=".", extra_args=lli_args),
ToolSubst("%llc_dwarf", FindTool("llc"), extra_args=llc_args),
ToolSubst("%gold", config.gold_executable, unresolved="ignore"),
ToolSubst("%ld64", ld64_cmd, unresolved="ignore"),
ToolSubst("%ocamlc", ocamlc_command, unresolved="ignore"),
ToolSubst("%ocamlopt", ocamlopt_command, unresolved="ignore"),
ToolSubst("%opt-viewer", opt_viewer_cmd),
ToolSubst("%llvm-objcopy", FindTool("llvm-objcopy")),
ToolSubst("%llvm-strip", FindTool("llvm-strip")),
ToolSubst("%llvm-install-name-tool", FindTool("llvm-install-name-tool")),
ToolSubst("%llvm-bitcode-strip", FindTool("llvm-bitcode-strip")),
ToolSubst("%split-file", FindTool("split-file")),
]
# FIXME: Why do we have both `lli` and `%lli` that do slightly different things?
tools.extend([
'dsymutil', 'lli', 'lli-child-target', 'llvm-ar', 'llvm-as',
'llvm-addr2line', 'llvm-bcanalyzer', 'llvm-bitcode-strip', 'llvm-config',
'llvm-cov', 'llvm-cxxdump', 'llvm-cvtres', 'llvm-debuginfod-find',
'llvm-debuginfo-analyzer',
'llvm-diff', 'llvm-dis', 'llvm-dwarfdump', 'llvm-dwarfutil', 'llvm-dlltool',
'llvm-exegesis', 'llvm-extract', 'llvm-isel-fuzzer', 'llvm-ifs',
'llvm-install-name-tool', 'llvm-jitlink', 'llvm-opt-fuzzer', 'llvm-lib',
'llvm-link', 'llvm-lto', 'llvm-lto2', 'llvm-mc', 'llvm-mca',
'llvm-modextract', 'llvm-nm', 'llvm-objcopy', 'llvm-objdump', 'llvm-otool',
'llvm-pdbutil', 'llvm-profdata', 'llvm-profgen', 'llvm-ranlib', 'llvm-rc', 'llvm-readelf',
'llvm-readobj', 'llvm-remark-size-diff', 'llvm-rtdyld', 'llvm-sim',
'llvm-size', 'llvm-split', 'llvm-stress', 'llvm-strings', 'llvm-strip',
'llvm-tblgen', 'llvm-tapi-diff', 'llvm-undname', 'llvm-windres',
'llvm-c-test', 'llvm-cxxfilt', 'llvm-xray', 'yaml2obj', 'obj2yaml',
'yaml-bench', 'verify-uselistorder', 'bugpoint', 'llc', 'llvm-symbolizer',
'opt', 'sancov', 'sanstats', 'llvm-remarkutil'])
tools.extend(
[
"dsymutil",
"lli",
"lli-child-target",
"llvm-ar",
"llvm-as",
"llvm-addr2line",
"llvm-bcanalyzer",
"llvm-bitcode-strip",
"llvm-config",
"llvm-cov",
"llvm-cxxdump",
"llvm-cvtres",
"llvm-debuginfod-find",
"llvm-debuginfo-analyzer",
"llvm-diff",
"llvm-dis",
"llvm-dwarfdump",
"llvm-dwarfutil",
"llvm-dlltool",
"llvm-exegesis",
"llvm-extract",
"llvm-isel-fuzzer",
"llvm-ifs",
"llvm-install-name-tool",
"llvm-jitlink",
"llvm-opt-fuzzer",
"llvm-lib",
"llvm-link",
"llvm-lto",
"llvm-lto2",
"llvm-mc",
"llvm-mca",
"llvm-modextract",
"llvm-nm",
"llvm-objcopy",
"llvm-objdump",
"llvm-otool",
"llvm-pdbutil",
"llvm-profdata",
"llvm-profgen",
"llvm-ranlib",
"llvm-rc",
"llvm-readelf",
"llvm-readobj",
"llvm-remark-size-diff",
"llvm-rtdyld",
"llvm-sim",
"llvm-size",
"llvm-split",
"llvm-stress",
"llvm-strings",
"llvm-strip",
"llvm-tblgen",
"llvm-tapi-diff",
"llvm-undname",
"llvm-windres",
"llvm-c-test",
"llvm-cxxfilt",
"llvm-xray",
"yaml2obj",
"obj2yaml",
"yaml-bench",
"verify-uselistorder",
"bugpoint",
"llc",
"llvm-symbolizer",
"opt",
"sancov",
"sanstats",
"llvm-remarkutil",
]
)
# The following tools are optional
tools.extend([
ToolSubst('llvm-mt', unresolved='ignore'),
ToolSubst('llvm-debuginfod', unresolved='ignore'),
ToolSubst('Kaleidoscope-Ch3', unresolved='ignore'),
ToolSubst('Kaleidoscope-Ch4', unresolved='ignore'),
ToolSubst('Kaleidoscope-Ch5', unresolved='ignore'),
ToolSubst('Kaleidoscope-Ch6', unresolved='ignore'),
ToolSubst('Kaleidoscope-Ch7', unresolved='ignore'),
ToolSubst('Kaleidoscope-Ch8', unresolved='ignore'),
ToolSubst('LLJITWithThinLTOSummaries', unresolved='ignore'),
ToolSubst('LLJITWithRemoteDebugging', unresolved='ignore'),
ToolSubst('OrcV2CBindingsBasicUsage', unresolved='ignore'),
ToolSubst('OrcV2CBindingsAddObjectFile', unresolved='ignore'),
ToolSubst('OrcV2CBindingsRemovableCode', unresolved='ignore'),
ToolSubst('OrcV2CBindingsLazy', unresolved='ignore'),
ToolSubst('OrcV2CBindingsVeryLazy', unresolved='ignore'),
ToolSubst('dxil-dis', unresolved='ignore')])
tools.extend(
[
ToolSubst("llvm-mt", unresolved="ignore"),
ToolSubst("llvm-debuginfod", unresolved="ignore"),
ToolSubst("Kaleidoscope-Ch3", unresolved="ignore"),
ToolSubst("Kaleidoscope-Ch4", unresolved="ignore"),
ToolSubst("Kaleidoscope-Ch5", unresolved="ignore"),
ToolSubst("Kaleidoscope-Ch6", unresolved="ignore"),
ToolSubst("Kaleidoscope-Ch7", unresolved="ignore"),
ToolSubst("Kaleidoscope-Ch8", unresolved="ignore"),
ToolSubst("LLJITWithThinLTOSummaries", unresolved="ignore"),
ToolSubst("LLJITWithRemoteDebugging", unresolved="ignore"),
ToolSubst("OrcV2CBindingsBasicUsage", unresolved="ignore"),
ToolSubst("OrcV2CBindingsAddObjectFile", unresolved="ignore"),
ToolSubst("OrcV2CBindingsRemovableCode", unresolved="ignore"),
ToolSubst("OrcV2CBindingsLazy", unresolved="ignore"),
ToolSubst("OrcV2CBindingsVeryLazy", unresolved="ignore"),
ToolSubst("dxil-dis", unresolved="ignore"),
]
)
# Find (major, minor) version of ptxas
def ptxas_version(ptxas):
ptxas_cmd = subprocess.Popen([ptxas, '--version'], stdout=subprocess.PIPE)
ptxas_out = ptxas_cmd.stdout.read().decode('ascii')
ptxas_cmd = subprocess.Popen([ptxas, "--version"], stdout=subprocess.PIPE)
ptxas_out = ptxas_cmd.stdout.read().decode("ascii")
ptxas_cmd.wait()
match = re.search('release (\d+)\.(\d+)', ptxas_out)
match = re.search("release (\d+)\.(\d+)", ptxas_out)
if match:
return (int(match.group(1)), int(match.group(2)))
print('couldn\'t determine ptxas version')
print("couldn't determine ptxas version")
return None
# Enable %ptxas and %ptxas-verify tools.
# %ptxas-verify defaults to sm_60 architecture. It can be overriden
# by specifying required one, for instance: %ptxas-verify -arch=sm_80.
@ -213,10 +289,22 @@ def enable_ptxas(ptxas_executable):
# versions, so add a feature for every known version prior to
# the current one.
ptxas_known_versions = [
(9, 0), (9, 1), (9, 2),
(10, 0), (10, 1), (10, 2),
(11, 0), (11, 1), (11, 2), (11, 3), (11, 4), (11, 5), (11, 6),
(11, 7), (11, 8), (12, 0),
(9, 0),
(9, 1),
(9, 2),
(10, 0),
(10, 1),
(10, 2),
(11, 0),
(11, 1),
(11, 2),
(11, 3),
(11, 4),
(11, 5),
(11, 6),
(11, 7),
(11, 8),
(12, 0),
]
def version_int(ver):
@ -227,23 +315,29 @@ def enable_ptxas(ptxas_executable):
min_version = ptxas_known_versions[0]
if version_int(version) < version_int(min_version):
print(
'Warning: ptxas version {}.{} is not supported'.format(
version[0], version[1]))
"Warning: ptxas version {}.{} is not supported".format(
version[0], version[1]
)
)
return
for known_version in ptxas_known_versions:
if version_int(known_version) <= version_int(version):
major, minor = known_version
config.available_features.add(
'ptxas-{}.{}'.format(major, minor))
config.available_features.add("ptxas-{}.{}".format(major, minor))
config.available_features.add('ptxas')
tools.extend([ToolSubst('%ptxas', ptxas_executable),
ToolSubst('%ptxas-verify', '{} -arch=sm_60 -c -'.format(
ptxas_executable))])
config.available_features.add("ptxas")
tools.extend(
[
ToolSubst("%ptxas", ptxas_executable),
ToolSubst("%ptxas-verify", "{} -arch=sm_60 -c -".format(ptxas_executable)),
]
)
ptxas_executable = \
os.environ.get('LLVM_PTXAS_EXECUTABLE', None) or config.ptxas_executable
ptxas_executable = (
os.environ.get("LLVM_PTXAS_EXECUTABLE", None) or config.ptxas_executable
)
if ptxas_executable:
enable_ptxas(ptxas_executable)
@ -254,63 +348,78 @@ llvm_config.add_tool_substitutions(tools, config.llvm_tools_dir)
config.targets = frozenset(config.targets_to_build.split())
for arch in config.targets_to_build.split():
config.available_features.add(arch.lower() + '-registered-target')
config.available_features.add(arch.lower() + "-registered-target")
# Features
known_arches = ["x86_64", "mips64", "ppc64", "aarch64"]
if (config.host_ldflags.find("-m32") < 0
and any(config.llvm_host_triple.startswith(x) for x in known_arches)):
config.available_features.add("llvm-64-bits")
if config.host_ldflags.find("-m32") < 0 and any(
config.llvm_host_triple.startswith(x) for x in known_arches
):
config.available_features.add("llvm-64-bits")
config.available_features.add("host-byteorder-" + sys.byteorder + "-endian")
if sys.platform in ['win32']:
if sys.platform in ["win32"]:
# ExecutionEngine, no weak symbols in COFF.
config.available_features.add('uses_COFF')
config.available_features.add("uses_COFF")
else:
# Others/can-execute.txt
config.available_features.add('can-execute')
config.available_features.add("can-execute")
# Loadable module
if config.has_plugins:
config.available_features.add('plugins')
config.available_features.add("plugins")
if config.build_examples:
config.available_features.add('examples')
config.available_features.add("examples")
if config.linked_bye_extension:
config.substitutions.append(('%llvmcheckext', 'CHECK-EXT'))
config.substitutions.append(('%loadbye', ''))
config.substitutions.append(('%loadnewpmbye', ''))
config.substitutions.append(("%llvmcheckext", "CHECK-EXT"))
config.substitutions.append(("%loadbye", ""))
config.substitutions.append(("%loadnewpmbye", ""))
else:
config.substitutions.append(('%llvmcheckext', 'CHECK-NOEXT'))
config.substitutions.append(('%loadbye',
'-load={}/Bye{}'.format(config.llvm_shlib_dir,
config.llvm_shlib_ext)))
config.substitutions.append(('%loadnewpmbye',
'-load-pass-plugin={}/Bye{}'
.format(config.llvm_shlib_dir,
config.llvm_shlib_ext)))
config.substitutions.append(("%llvmcheckext", "CHECK-NOEXT"))
config.substitutions.append(
(
"%loadbye",
"-load={}/Bye{}".format(config.llvm_shlib_dir, config.llvm_shlib_ext),
)
)
config.substitutions.append(
(
"%loadnewpmbye",
"-load-pass-plugin={}/Bye{}".format(
config.llvm_shlib_dir, config.llvm_shlib_ext
),
)
)
if config.linked_exampleirtransforms_extension:
config.substitutions.append(('%loadexampleirtransforms',''))
config.substitutions.append(("%loadexampleirtransforms", ""))
else:
config.substitutions.append(('%loadexampleirtransforms',
'-load-pass-plugin={}/ExampleIRTransforms{}'
.format(config.llvm_shlib_dir,
config.llvm_shlib_ext)))
config.substitutions.append(
(
"%loadexampleirtransforms",
"-load-pass-plugin={}/ExampleIRTransforms{}".format(
config.llvm_shlib_dir, config.llvm_shlib_ext
),
)
)
# Static libraries are not built if BUILD_SHARED_LIBS is ON.
if not config.build_shared_libs and not config.link_llvm_dylib:
config.available_features.add('static-libs')
config.available_features.add("static-libs")
if config.link_llvm_dylib:
config.available_features.add('llvm-dylib')
config.available_features.add("llvm-dylib")
config.substitutions.append(
('%llvmdylib',
'{}/libLLVM-{}{}'.format(config.llvm_shlib_dir,
config.llvm_dylib_version,
config.llvm_shlib_ext)))
(
"%llvmdylib",
"{}/libLLVM-{}{}".format(
config.llvm_shlib_dir, config.llvm_dylib_version, config.llvm_shlib_ext
),
)
)
if config.have_tf_aot:
config.available_features.add("have_tf_aot")
@ -324,86 +433,93 @@ if config.llvm_inliner_model_autogenerated:
if config.llvm_raevict_model_autogenerated:
config.available_features.add("llvm_raevict_model_autogenerated")
def have_cxx_shared_library():
readobj_exe = lit.util.which('llvm-readobj', config.llvm_tools_dir)
readobj_exe = lit.util.which("llvm-readobj", config.llvm_tools_dir)
if not readobj_exe:
print('llvm-readobj not found')
print("llvm-readobj not found")
return False
try:
readobj_cmd = subprocess.Popen(
[readobj_exe, '--needed-libs', readobj_exe], stdout=subprocess.PIPE)
[readobj_exe, "--needed-libs", readobj_exe], stdout=subprocess.PIPE
)
except OSError:
print('could not exec llvm-readobj')
print("could not exec llvm-readobj")
return False
readobj_out = readobj_cmd.stdout.read().decode('ascii')
readobj_out = readobj_cmd.stdout.read().decode("ascii")
readobj_cmd.wait()
regex = re.compile(r'(libc\+\+|libstdc\+\+|msvcp).*\.(so|dylib|dll)')
regex = re.compile(r"(libc\+\+|libstdc\+\+|msvcp).*\.(so|dylib|dll)")
needed_libs = False
for line in readobj_out.splitlines():
if 'NeededLibraries [' in line:
if "NeededLibraries [" in line:
needed_libs = True
if ']' in line:
if "]" in line:
needed_libs = False
if needed_libs and regex.search(line.lower()):
return True
return False
if have_cxx_shared_library():
config.available_features.add('cxx-shared-library')
config.available_features.add("cxx-shared-library")
if config.libcxx_used:
config.available_features.add('libcxx-used')
config.available_features.add("libcxx-used")
# LLVM can be configured with an empty default triple
# Some tests are "generic" and require a valid default triple
if config.target_triple:
config.available_features.add('default_triple')
config.available_features.add("default_triple")
# Direct object generation
if not config.target_triple.startswith(("nvptx", "xcore")):
config.available_features.add('object-emission')
config.available_features.add("object-emission")
# Allow checking for specific details in the host triple
if config.host_triple:
config.available_features.add('host=%s' % config.host_triple)
if config.have_llvm_driver:
config.available_features.add('llvm-driver')
config.available_features.add("llvm-driver")
import subprocess
def have_ld_plugin_support():
if not os.path.exists(os.path.join(config.llvm_shlib_dir, 'LLVMgold' + config.llvm_shlib_ext)):
if not os.path.exists(
os.path.join(config.llvm_shlib_dir, "LLVMgold" + config.llvm_shlib_ext)
):
return False
ld_cmd = subprocess.Popen(
[config.gold_executable, '--help'], stdout=subprocess.PIPE, env={'LANG': 'C'})
[config.gold_executable, "--help"], stdout=subprocess.PIPE, env={"LANG": "C"}
)
ld_out = ld_cmd.stdout.read().decode()
ld_cmd.wait()
if not '-plugin' in ld_out:
if not "-plugin" in ld_out:
return False
# check that the used emulations are supported.
emu_line = [l for l in ld_out.split('\n') if 'supported emulations' in l]
emu_line = [l for l in ld_out.split("\n") if "supported emulations" in l]
if len(emu_line) != 1:
return False
emu_line = emu_line[0]
fields = emu_line.split(':')
fields = emu_line.split(":")
if len(fields) != 3:
return False
emulations = fields[2].split()
if 'elf_x86_64' not in emulations:
if "elf_x86_64" not in emulations:
return False
if 'elf32ppc' in emulations:
config.available_features.add('ld_emu_elf32ppc')
if "elf32ppc" in emulations:
config.available_features.add("ld_emu_elf32ppc")
ld_version = subprocess.Popen(
[config.gold_executable, '--version'], stdout=subprocess.PIPE, env={'LANG': 'C'})
if not 'GNU gold' in ld_version.stdout.read().decode():
[config.gold_executable, "--version"], stdout=subprocess.PIPE, env={"LANG": "C"}
)
if not "GNU gold" in ld_version.stdout.read().decode():
return False
ld_version.wait()
@ -411,94 +527,113 @@ def have_ld_plugin_support():
if have_ld_plugin_support():
config.available_features.add('ld_plugin')
config.available_features.add("ld_plugin")
def have_ld64_plugin_support():
if not os.path.exists(os.path.join(config.llvm_shlib_dir, 'libLTO' + config.llvm_shlib_ext)):
if not os.path.exists(
os.path.join(config.llvm_shlib_dir, "libLTO" + config.llvm_shlib_ext)
):
return False
if config.ld64_executable == '':
if config.ld64_executable == "":
return False
ld_cmd = subprocess.Popen(
[config.ld64_executable, '-v'], stderr=subprocess.PIPE)
ld_cmd = subprocess.Popen([config.ld64_executable, "-v"], stderr=subprocess.PIPE)
ld_out = ld_cmd.stderr.read().decode()
ld_cmd.wait()
if 'ld64' not in ld_out or 'LTO' not in ld_out:
if "ld64" not in ld_out or "LTO" not in ld_out:
return False
return True
if have_ld64_plugin_support():
config.available_features.add('ld64_plugin')
config.available_features.add("ld64_plugin")
# Ask llvm-config about asserts
llvm_config.feature_config(
[('--assertion-mode', {'ON': 'asserts'}),
('--build-mode', {'[Dd][Ee][Bb][Uu][Gg]': 'debug'})])
[
("--assertion-mode", {"ON": "asserts"}),
("--build-mode", {"[Dd][Ee][Bb][Uu][Gg]": "debug"}),
]
)
if 'darwin' == sys.platform:
cmd = ['sysctl', 'hw.optional.fma']
if "darwin" == sys.platform:
cmd = ["sysctl", "hw.optional.fma"]
sysctl_cmd = subprocess.Popen(cmd, stdout=subprocess.PIPE)
# Non zero return, probably a permission issue
if sysctl_cmd.wait():
print(
"Warning: sysctl exists but calling \"{}\" failed, defaulting to no fma3.".format(
" ".join(cmd)))
'Warning: sysctl exists but calling "{}" failed, defaulting to no fma3.'.format(
" ".join(cmd)
)
)
else:
result = sysctl_cmd.stdout.read().decode('ascii')
if 'hw.optional.fma: 1' in result:
config.available_features.add('fma3')
result = sysctl_cmd.stdout.read().decode("ascii")
if "hw.optional.fma: 1" in result:
config.available_features.add("fma3")
# .debug_frame is not emitted for targeting Windows x64, aarch64/arm64, AIX, or Apple Silicon Mac.
if not re.match(r'^(x86_64|aarch64|arm64|powerpc|powerpc64).*-(windows-gnu|windows-msvc|aix)', config.target_triple) \
and not re.match(r'^arm64(e)?-apple-(macos|darwin)', config.target_triple):
config.available_features.add('debug_frame')
if not re.match(
r"^(x86_64|aarch64|arm64|powerpc|powerpc64).*-(windows-gnu|windows-msvc|aix)",
config.target_triple,
) and not re.match(r"^arm64(e)?-apple-(macos|darwin)", config.target_triple):
config.available_features.add("debug_frame")
if config.have_libxar:
config.available_features.add('xar')
config.available_features.add("xar")
if config.enable_threads:
config.available_features.add('thread_support')
config.available_features.add("thread_support")
if config.have_libxml2:
config.available_features.add('libxml2')
config.available_features.add("libxml2")
if config.have_curl:
config.available_features.add('curl')
config.available_features.add("curl")
if config.have_httplib:
config.available_features.add('httplib')
config.available_features.add("httplib")
if config.have_opt_viewer_modules:
config.available_features.add('have_opt_viewer_modules')
config.available_features.add("have_opt_viewer_modules")
if config.expensive_checks:
config.available_features.add('expensive_checks')
config.available_features.add("expensive_checks")
if "MemoryWithOrigins" in config.llvm_use_sanitizer:
config.available_features.add('use_msan_with_origins')
config.available_features.add("use_msan_with_origins")
def exclude_unsupported_files_for_aix(dirname):
for filename in os.listdir(dirname):
source_path = os.path.join( dirname, filename)
if os.path.isdir(source_path):
continue
f = open(source_path, 'r')
try:
data = f.read()
# 64-bit object files are not supported on AIX, so exclude the tests.
if ('-emit-obj' in data or '-filetype=obj' in data) and '64' in config.target_triple:
config.excludes += [ filename ]
finally:
f.close()
for filename in os.listdir(dirname):
source_path = os.path.join(dirname, filename)
if os.path.isdir(source_path):
continue
f = open(source_path, "r")
try:
data = f.read()
# 64-bit object files are not supported on AIX, so exclude the tests.
if (
"-emit-obj" in data or "-filetype=obj" in data
) and "64" in config.target_triple:
config.excludes += [filename]
finally:
f.close()
if 'aix' in config.target_triple:
for directory in ('/CodeGen/X86', '/DebugInfo', '/DebugInfo/X86', '/DebugInfo/Generic', '/LTO/X86', '/Linker'):
if "aix" in config.target_triple:
for directory in (
"/CodeGen/X86",
"/DebugInfo",
"/DebugInfo/X86",
"/DebugInfo/Generic",
"/LTO/X86",
"/Linker",
):
exclude_unsupported_files_for_aix(config.test_source_root + directory)
# Some tools support an environment variable "OBJECT_MODE" on AIX OS, which
@ -507,5 +642,5 @@ if 'aix' in config.target_triple:
# objects only. In order to not affect most test cases, which expect to support
# 32-bit and 64-bit objects by default, set the environment variable
# "OBJECT_MODE" to 'any' by default on AIX OS.
if 'system-aix' in config.available_features:
config.environment['OBJECT_MODE'] = 'any'
if "system-aix" in config.available_features:
config.environment["OBJECT_MODE"] = "any"

View File

@ -4,20 +4,22 @@ import subprocess
import sys
import threading
class TrivialHandler(http.server.BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(501)
def do_GET(self):
self.send_response(501)
def log_request(self, *args, **kwargs):
print(self.requestline)
print(self.headers)
def log_request(self, *args, **kwargs):
print(self.requestline)
print(self.headers)
httpd = http.server.HTTPServer(('', 0), TrivialHandler)
httpd = http.server.HTTPServer(("", 0), TrivialHandler)
port = httpd.socket.getsockname()[1]
try:
t = threading.Thread(target=httpd.serve_forever).start()
os.environ['DEBUGINFOD_URLS'] =f'http://localhost:{port}'
subprocess.run(sys.argv[1:], capture_output = True)
t = threading.Thread(target=httpd.serve_forever).start()
os.environ["DEBUGINFOD_URLS"] = f"http://localhost:{port}"
subprocess.run(sys.argv[1:], capture_output=True)
finally:
httpd.shutdown()
httpd.shutdown()

View File

@ -7,17 +7,17 @@ import sys
f = open(sys.argv[1], "rb")
byte = f.read(1)
while byte != b'':
if byte == b'\x00':
while byte != b"":
if byte == b"\x00":
sys.stdout.write("version: ")
elif byte == b'\x10':
elif byte == b"\x10":
sys.stdout.write("input-file: ")
elif byte == b'\x11':
elif byte == b"\x11":
sys.stdout.write("not-found: ")
elif byte == b'\x40':
elif byte == b"\x40":
sys.stdout.write("output-file: ")
byte = f.read(1)
while byte != b'\x00':
while byte != b"\x00":
sys.stdout.write(byte.decode("ascii"))
byte = f.read(1)
sys.stdout.write("\n")

View File

@ -1,13 +1,14 @@
import gzip
import sys
with gzip.open(sys.argv[1], 'rb') as f:
writer = getattr(sys.stdout, 'buffer', None)
if writer is None:
writer = sys.stdout
if sys.platform == "win32":
import os, msvcrt
msvcrt.setmode(sys.stdout.fileno(),os.O_BINARY)
with gzip.open(sys.argv[1], "rb") as f:
writer = getattr(sys.stdout, "buffer", None)
if writer is None:
writer = sys.stdout
if sys.platform == "win32":
import os, msvcrt
writer.write(f.read())
sys.stdout.flush()
msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
writer.write(f.read())
sys.stdout.flush()

View File

@ -24,15 +24,25 @@ import struct
import sys
import typing
class CodeDirectoryVersion:
SUPPORTSSCATTER = 0x20100
SUPPORTSTEAMID = 0x20200
SUPPORTSCODELIMIT64 = 0x20300
SUPPORTSEXECSEG = 0x20400
class CodeDirectory:
@staticmethod
def make(buf: memoryview) -> typing.Union['CodeDirectoryBase', 'CodeDirectoryV20100', 'CodeDirectoryV20200', 'CodeDirectoryV20300', 'CodeDirectoryV20400']:
def make(
buf: memoryview,
) -> typing.Union[
"CodeDirectoryBase",
"CodeDirectoryV20100",
"CodeDirectoryV20200",
"CodeDirectoryV20300",
"CodeDirectoryV20400",
]:
_magic, _length, version = struct.unpack_from(">III", buf, 0)
subtype = {
CodeDirectoryVersion.SUPPORTSSCATTER: CodeDirectoryV20100,
@ -43,6 +53,7 @@ class CodeDirectory:
return subtype._make(struct.unpack_from(subtype._format(), buf, 0))
class CodeDirectoryBase(typing.NamedTuple):
magic: int
length: int
@ -63,6 +74,7 @@ class CodeDirectoryBase(typing.NamedTuple):
def _format() -> str:
return ">IIIIIIIIIBBBBI"
class CodeDirectoryV20100(typing.NamedTuple):
magic: int
length: int
@ -85,6 +97,7 @@ class CodeDirectoryV20100(typing.NamedTuple):
def _format() -> str:
return CodeDirectoryBase._format() + "I"
class CodeDirectoryV20200(typing.NamedTuple):
magic: int
length: int
@ -109,6 +122,7 @@ class CodeDirectoryV20200(typing.NamedTuple):
def _format() -> str:
return CodeDirectoryV20100._format() + "I"
class CodeDirectoryV20300(typing.NamedTuple):
magic: int
length: int
@ -136,6 +150,7 @@ class CodeDirectoryV20300(typing.NamedTuple):
def _format() -> str:
return CodeDirectoryV20200._format() + "IQ"
class CodeDirectoryV20400(typing.NamedTuple):
magic: int
length: int
@ -167,13 +182,16 @@ class CodeDirectoryV20400(typing.NamedTuple):
def _format() -> str:
return CodeDirectoryV20300._format() + "QQQ"
class CodeDirectoryBlobIndex(typing.NamedTuple):
type_: int
offset: int
@staticmethod
def make(buf: memoryview) -> 'CodeDirectoryBlobIndex':
return CodeDirectoryBlobIndex._make(struct.unpack_from(CodeDirectoryBlobIndex.__format(), buf, 0))
def make(buf: memoryview) -> "CodeDirectoryBlobIndex":
return CodeDirectoryBlobIndex._make(
struct.unpack_from(CodeDirectoryBlobIndex.__format(), buf, 0)
)
@staticmethod
def bytesize() -> int:
@ -183,6 +201,7 @@ class CodeDirectoryBlobIndex(typing.NamedTuple):
def __format() -> str:
return ">II"
class CodeDirectorySuperBlob(typing.NamedTuple):
magic: int
length: int
@ -190,7 +209,7 @@ class CodeDirectorySuperBlob(typing.NamedTuple):
blob_indices: typing.List[CodeDirectoryBlobIndex]
@staticmethod
def make(buf: memoryview) -> 'CodeDirectorySuperBlob':
def make(buf: memoryview) -> "CodeDirectorySuperBlob":
super_blob_layout = ">III"
super_blob = struct.unpack_from(super_blob_layout, buf, 0)
@ -202,17 +221,25 @@ class CodeDirectorySuperBlob(typing.NamedTuple):
return CodeDirectorySuperBlob(*super_blob, blob_indices)
def unpack_null_terminated_string(buf: memoryview) -> str:
b = bytes(itertools.takewhile(lambda b: b != 0, buf))
return b.decode()
def main():
parser = argparse.ArgumentParser()
parser.add_argument('binary', type=argparse.FileType('rb'), help='The file to analyze')
parser.add_argument('offset', type=int, help='Offset to start of Code Directory data')
parser.add_argument('size', type=int, help='Size of Code Directory data')
parser.add_argument('code_offset', type=int, help='Offset to start of code pages to hash')
parser.add_argument('code_size', type=int, help='Size of the code pages to hash')
parser.add_argument(
"binary", type=argparse.FileType("rb"), help="The file to analyze"
)
parser.add_argument(
"offset", type=int, help="Offset to start of Code Directory data"
)
parser.add_argument("size", type=int, help="Size of Code Directory data")
parser.add_argument(
"code_offset", type=int, help="Offset to start of code pages to hash"
)
parser.add_argument("code_size", type=int, help="Size of the code pages to hash")
args = parser.parse_args()
@ -229,7 +256,10 @@ def main():
print(code_directory)
ident_offset = code_directory_offset + code_directory.identOffset
print("Code Directory ID: " + unpack_null_terminated_string(super_blob_mem[ident_offset:]))
print(
"Code Directory ID: "
+ unpack_null_terminated_string(super_blob_mem[ident_offset:])
)
code_offset = args.code_offset
code_end = code_offset + args.code_size
@ -238,7 +268,9 @@ def main():
hashes_offset = code_directory_offset + code_directory.hashOffset
for idx in range(code_directory.nCodeSlots):
hash_bytes = bytes(super_blob_mem[hashes_offset:hashes_offset+code_directory.hashSize])
hash_bytes = bytes(
super_blob_mem[hashes_offset : hashes_offset + code_directory.hashSize]
)
hashes_offset += code_directory.hashSize
hasher = hashlib.sha256()
@ -253,5 +285,5 @@ def main():
sys.exit(-1)
if __name__ == '__main__':
if __name__ == "__main__":
main()

View File

@ -18,7 +18,9 @@ import subprocess
llvm_dis = sys.argv[1]
filecheck = sys.argv[2]
filecheck_args = [filecheck, ]
filecheck_args = [
filecheck,
]
filecheck_args.extend(sys.argv[3:-1])
bitcode_file = sys.argv[-1]
ir_file = bitcode_file + ".ll"
@ -36,7 +38,7 @@ if disassemble.returncode != 0:
print(disassemble.stdout)
sys.exit(1)
check=None
check = None
with open(ir_file, "r") as ir:
check = subprocess.Popen(filecheck_args, stdin=ir, stdout=sys.stdout)
check.communicate()

View File

@ -5,12 +5,12 @@ FunctionCallPresent = False
input = open(sys.argv[1], "r")
for line in input:
if "%interesting" in line:
InterestingArgumentPresent = True
if "call void @interesting" in line:
FunctionCallPresent = True
if "%interesting" in line:
InterestingArgumentPresent = True
if "call void @interesting" in line:
FunctionCallPresent = True
if InterestingArgumentPresent and FunctionCallPresent:
sys.exit(0) # Interesting!
sys.exit(0) # Interesting!
sys.exit(1)

View File

@ -3,13 +3,13 @@ import sys
InterestingBBs = 0
input = open(sys.argv[1], "r")
for line in input:
i = line.find(';')
if i >= 0:
line = line[:i]
if line.startswith("interesting") or "%interesting" in line:
InterestingBBs += 1
i = line.find(";")
if i >= 0:
line = line[:i]
if line.startswith("interesting") or "%interesting" in line:
InterestingBBs += 1
if InterestingBBs == 6:
sys.exit(0) # interesting!
sys.exit(0) # interesting!
sys.exit(1) # IR isn't interesting
sys.exit(1) # IR isn't interesting

View File

@ -4,14 +4,14 @@ InterestingInstructions = 0
input = open(sys.argv[1], "r")
for line in input:
i = line.find(';')
if i >= 0:
line = line[:i]
if "%interesting" in line:
InterestingInstructions += 1
print(InterestingInstructions)
i = line.find(";")
if i >= 0:
line = line[:i]
if "%interesting" in line:
InterestingInstructions += 1
print(InterestingInstructions)
if InterestingInstructions == 5:
sys.exit(0) # interesting!
sys.exit(0) # interesting!
sys.exit(1)

View File

@ -10,8 +10,8 @@ file_input = sys.argv[3]
try:
input = open(file_input, "r")
except Exception as err:
print(err, file=sys.stderr)
sys.exit(1)
print(err, file=sys.stderr)
sys.exit(1)
InterestingStores = 0
for line in input:
@ -23,6 +23,6 @@ time.sleep(sleep_seconds)
if InterestingStores > num_stores:
sys.exit(0) # interesting!
sys.exit(0) # interesting!
sys.exit(1) # IR isn't interesting
sys.exit(1) # IR isn't interesting

View File

@ -1,15 +1,19 @@
import subprocess
import sys
opt = subprocess.run( [ 'opt', '-passes=print<loops>','-disable-output', sys.argv[1]], stdout=subprocess.PIPE, stderr=subprocess.PIPE )
opt = subprocess.run(
["opt", "-passes=print<loops>", "-disable-output", sys.argv[1]],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
stdout = opt.stdout.decode()
pattern = 'Loop at depth 1 containing'
pattern = "Loop at depth 1 containing"
if (pattern in opt.stderr.decode()):
print('This is interesting!')
sys.exit(0)
if pattern in opt.stderr.decode():
print("This is interesting!")
sys.exit(0)
else:
print('This is NOT interesting!')
sys.exit(1)
print("This is NOT interesting!")
sys.exit(1)

View File

@ -4,21 +4,24 @@ import subprocess
import sys
import threading
def kill_subprocess(process):
process.kill()
os._exit(1)
# Pass -f=none and --output-style=GNU to get only one line of output per input.
cmd = subprocess.Popen([sys.argv[1],
'--obj=' + sys.argv[2],
'-f=none',
'--output-style=GNU'], stdout=subprocess.PIPE, stdin=subprocess.PIPE)
cmd = subprocess.Popen(
[sys.argv[1], "--obj=" + sys.argv[2], "-f=none", "--output-style=GNU"],
stdout=subprocess.PIPE,
stdin=subprocess.PIPE,
)
watchdog = threading.Timer(20, kill_subprocess, args=[cmd])
watchdog.start()
cmd.stdin.write(b'0\n')
cmd.stdin.write(b"0\n")
cmd.stdin.flush()
print(cmd.stdout.readline())
cmd.stdin.write(b'bad\n')
cmd.stdin.write(b"bad\n")
cmd.stdin.flush()
print(cmd.stdout.readline())
watchdog.cancel()

View File

@ -1,10 +1,10 @@
#===- gen-msvc-exports.py - Generate C API export file -------*- python -*--===#
# ===- gen-msvc-exports.py - Generate C API export file -------*- python -*--===#
#
# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
# See https://llvm.org/LICENSE.txt for license information.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#
#===------------------------------------------------------------------------===#
# ===------------------------------------------------------------------------===#
#
# Generate an export file from a list of given LIB files. This only exports symbols
# that start with LLVM, so it only exports the LLVM C API.
@ -22,7 +22,7 @@
#
# You can use the --output flag to set the name of the export file.
#
#===------------------------------------------------------------------------===#
# ===------------------------------------------------------------------------===#
from tempfile import mkstemp
from contextlib import contextmanager
from subprocess import check_call
@ -33,7 +33,7 @@ import re
_UNDERSCORE_REGEX = {
False: re.compile(r"^\w+\s+T\s+(LLVM.*)$"),
True: re.compile(r"^\w+\s+T\s+_(LLVM.*)$")
True: re.compile(r"^\w+\s+T\s+_(LLVM.*)$"),
}
@ -58,46 +58,54 @@ def gen_llvm_c_export(output, underscore, libs, nm):
to `output`. If `underscore` is true, symbols will
be assumed to be prefixed with an underscore.
"""
with removing(touch_tempfile(prefix='dumpout', suffix='.txt')) as dumpout:
with removing(touch_tempfile(prefix="dumpout", suffix=".txt")) as dumpout:
# Get the right regex.
p = _UNDERSCORE_REGEX[underscore]
with open(output, 'w+t') as output_f:
with open(output, "w+t") as output_f:
# For each lib get the LLVM* functions it exports.
for lib in libs:
# Call dumpbin.
with open(dumpout, 'w+t') as dumpout_f:
check_call([nm, '-g', lib], stdout=dumpout_f)
with open(dumpout, "w+t") as dumpout_f:
check_call([nm, "-g", lib], stdout=dumpout_f)
# Get the matching lines.
with open(dumpout) as dumpbin:
for line in dumpbin:
m = p.match(line)
if m is not None:
output_f.write(m.group(1) + '\n')
output_f.write(m.group(1) + "\n")
def main():
parser = argparse.ArgumentParser('gen-msvc-exports')
parser = argparse.ArgumentParser("gen-msvc-exports")
parser.add_argument(
'-i', '--libsfile', help='file with list of libs, new line separated',
action='store', default=None
"-i",
"--libsfile",
help="file with list of libs, new line separated",
action="store",
default=None,
)
parser.add_argument(
'-o', '--output', help='output filename', default='LLVM-C.exports'
)
parser.add_argument('-u', '--underscore',
help='labels are prefixed with an underscore (use for 32 bit DLLs)',
action='store_true'
"-o", "--output", help="output filename", default="LLVM-C.exports"
)
parser.add_argument(
'--nm', help='path to the llvm-nm executable', default='llvm-nm'
"-u",
"--underscore",
help="labels are prefixed with an underscore (use for 32 bit DLLs)",
action="store_true",
)
parser.add_argument(
'libs', metavar='LIBS', nargs='*', help='list of libraries to generate export from'
"--nm", help="path to the llvm-nm executable", default="llvm-nm"
)
parser.add_argument(
"libs",
metavar="LIBS",
nargs="*",
help="list of libraries to generate export from",
)
ns = parser.parse_args()
@ -112,5 +120,5 @@ def main():
gen_llvm_c_export(ns.output, ns.underscore, libs, ns.nm)
if __name__ == '__main__':
if __name__ == "__main__":
main()

View File

@ -1,21 +1,22 @@
#!/usr/bin/env python
desc = '''
desc = """
A script to extract ConstraintElimination's reproducer remarks. The extracted
modules are written as textual LLVM IR to files named reproducerXXXX.ll in the
current directory.
'''
"""
import optrecord
import argparse
if __name__ == '__main__':
if __name__ == "__main__":
parser = argparse.ArgumentParser(description=desc)
parser.add_argument(
'yaml_dirs_or_files',
nargs='+',
help='List of optimization record files or directories searched '
'for optimization record files.')
"yaml_dirs_or_files",
nargs="+",
help="List of optimization record files or directories searched "
"for optimization record files.",
)
args = parser.parse_args()
@ -27,13 +28,12 @@ if __name__ == '__main__':
parser.error("No *.opt.yaml files found")
sys.exit(1)
all_remarks, file_remarks, _ = optrecord.gather_results(
files, jobs, True)
all_remarks, file_remarks, _ = optrecord.gather_results(files, jobs, True)
i = 0
for r in all_remarks:
if r[1] != 'constraint-elimination' or r[2] != 'Reproducer':
if r[1] != "constraint-elimination" or r[2] != "Reproducer":
continue
with open('reproducer{}.ll'.format(i), 'wt') as f:
with open("reproducer{}.ll".format(i), "wt") as f:
f.write(r[7][1][0][1])
i += 1

View File

@ -2,14 +2,15 @@
from __future__ import print_function
desc = '''Generate the difference of two YAML files into a new YAML file (works on
desc = """Generate the difference of two YAML files into a new YAML file (works on
pair of directories too). A new attribute 'Added' is set to True or False
depending whether the entry is added or removed from the first input to the
next.
The tools requires PyYAML.'''
The tools requires PyYAML."""
import yaml
# Try to use the C parser.
try:
from yaml import CLoader as Loader
@ -20,35 +21,40 @@ import optrecord
import argparse
from collections import defaultdict
if __name__ == '__main__':
if __name__ == "__main__":
parser = argparse.ArgumentParser(description=desc)
parser.add_argument(
'yaml_dir_or_file_1',
help='An optimization record file or a directory searched for optimization '
'record files that are used as the old version for the comparison')
"yaml_dir_or_file_1",
help="An optimization record file or a directory searched for optimization "
"record files that are used as the old version for the comparison",
)
parser.add_argument(
'yaml_dir_or_file_2',
help='An optimization record file or a directory searched for optimization '
'record files that are used as the new version for the comparison')
"yaml_dir_or_file_2",
help="An optimization record file or a directory searched for optimization "
"record files that are used as the new version for the comparison",
)
parser.add_argument(
'--jobs',
'-j',
"--jobs",
"-j",
default=None,
type=int,
help='Max job count (defaults to %(default)s, the current CPU count)')
help="Max job count (defaults to %(default)s, the current CPU count)",
)
parser.add_argument(
'--max-size',
'-m',
"--max-size",
"-m",
default=100000,
type=int,
help='Maximum number of remarks stored in an output file')
help="Maximum number of remarks stored in an output file",
)
parser.add_argument(
'--no-progress-indicator',
'-n',
action='store_true',
"--no-progress-indicator",
"-n",
action="store_true",
default=False,
help='Do not display any indicator of how many YAML files were read.')
parser.add_argument('--output', '-o', default='diff{}.opt.yaml')
help="Do not display any indicator of how many YAML files were read.",
)
parser.add_argument("--output", "-o", default="diff{}.opt.yaml")
args = parser.parse_args()
files1 = optrecord.find_opt_files(args.yaml_dir_or_file_1)
@ -71,5 +77,5 @@ if __name__ == '__main__':
r.recover_yaml_structure()
for i in range(0, len(result), args.max_size):
with open(args.output.format(i / args.max_size), 'w') as stream:
yaml.dump_all(result[i:i + args.max_size], stream)
with open(args.output.format(i / args.max_size), "w") as stream:
yaml.dump_all(result[i : i + args.max_size], stream)

View File

@ -2,10 +2,10 @@
from __future__ import print_function
desc = '''Generate statistics about optimization records from the YAML files
desc = """Generate statistics about optimization records from the YAML files
generated with -fsave-optimization-record and -fdiagnostics-show-hotness.
The tools requires PyYAML and Pygments Python packages.'''
The tools requires PyYAML and Pygments Python packages."""
import optrecord
import argparse
@ -15,30 +15,34 @@ from multiprocessing import cpu_count, Pool
try:
from guppy import hpy
hp = hpy()
except ImportError:
print("Memory consumption not shown because guppy is not installed")
hp = None
if __name__ == '__main__':
if __name__ == "__main__":
parser = argparse.ArgumentParser(description=desc)
parser.add_argument(
'yaml_dirs_or_files',
nargs='+',
help='List of optimization record files or directories searched '
'for optimization record files.')
"yaml_dirs_or_files",
nargs="+",
help="List of optimization record files or directories searched "
"for optimization record files.",
)
parser.add_argument(
'--jobs',
'-j',
"--jobs",
"-j",
default=None,
type=int,
help='Max job count (defaults to %(default)s, the current CPU count)')
help="Max job count (defaults to %(default)s, the current CPU count)",
)
parser.add_argument(
'--no-progress-indicator',
'-n',
action='store_true',
"--no-progress-indicator",
"-n",
action="store_true",
default=False,
help='Do not display any indicator of how many YAML files were read.')
help="Do not display any indicator of how many YAML files were read.",
)
args = parser.parse_args()
print_progress = not args.no_progress_indicator
@ -49,9 +53,10 @@ if __name__ == '__main__':
sys.exit(1)
all_remarks, file_remarks, _ = optrecord.gather_results(
files, args.jobs, print_progress)
files, args.jobs, print_progress
)
if print_progress:
print('\n')
print("\n")
bypass = defaultdict(int)
byname = defaultdict(int)
@ -63,16 +68,17 @@ if __name__ == '__main__':
print("{:24s} {:10d}".format("Total number of remarks", total))
if hp:
h = hp.heap()
print("{:24s} {:10d}".format("Memory per remark",
h.size / len(all_remarks)))
print('\n')
print("{:24s} {:10d}".format("Memory per remark", h.size / len(all_remarks)))
print("\n")
print("Top 10 remarks by pass:")
for (passname, count) in sorted(bypass.items(), key=operator.itemgetter(1),
reverse=True)[:10]:
print(" {:30s} {:2.0f}%". format(passname, count * 100. / total))
for (passname, count) in sorted(
bypass.items(), key=operator.itemgetter(1), reverse=True
)[:10]:
print(" {:30s} {:2.0f}%".format(passname, count * 100.0 / total))
print("\nTop 10 remarks:")
for (name, count) in sorted(byname.items(), key=operator.itemgetter(1),
reverse=True)[:10]:
print(" {:30s} {:2.0f}%". format(name, count * 100. / total))
for (name, count) in sorted(
byname.items(), key=operator.itemgetter(1), reverse=True
)[:10]:
print(" {:30s} {:2.0f}%".format(name, count * 100.0 / total))

View File

@ -21,27 +21,32 @@ import optpmap
import optrecord
desc = '''Generate HTML output to visualize optimization records from the YAML files
desc = """Generate HTML output to visualize optimization records from the YAML files
generated with -fsave-optimization-record and -fdiagnostics-show-hotness.
The tools requires PyYAML and Pygments Python packages.'''
The tools requires PyYAML and Pygments Python packages."""
# This allows passing the global context to the child processes.
class Context:
def __init__(self, caller_loc = dict()):
# Map function names to their source location for function where inlining happened
self.caller_loc = caller_loc
def __init__(self, caller_loc=dict()):
# Map function names to their source location for function where inlining happened
self.caller_loc = caller_loc
context = Context()
def suppress(remark):
if remark.Name == 'sil.Specialized':
return remark.getArgDict()['Function'][0].startswith('\"Swift.')
elif remark.Name == 'sil.Inlined':
return remark.getArgDict()['Callee'][0].startswith(('\"Swift.', '\"specialized Swift.'))
if remark.Name == "sil.Specialized":
return remark.getArgDict()["Function"][0].startswith('"Swift.')
elif remark.Name == "sil.Inlined":
return remark.getArgDict()["Callee"][0].startswith(
('"Swift.', '"specialized Swift.')
)
return False
class SourceFileRenderer:
def __init__(self, source_dir, output_dir, filename, no_highlight):
self.filename = filename
@ -54,18 +59,27 @@ class SourceFileRenderer:
existing_filename = fn
self.no_highlight = no_highlight
self.stream = io.open(os.path.join(output_dir, optrecord.html_file_name(filename)), 'w', encoding='utf-8')
self.stream = io.open(
os.path.join(output_dir, optrecord.html_file_name(filename)),
"w",
encoding="utf-8",
)
if existing_filename:
self.source_stream = io.open(existing_filename, encoding='utf-8')
self.source_stream = io.open(existing_filename, encoding="utf-8")
else:
self.source_stream = None
print(u'''
print(
"""
<html>
<h1>Unable to locate file {}</h1>
</html>
'''.format(filename), file=self.stream)
""".format(
filename
),
file=self.stream,
)
self.html_formatter = HtmlFormatter(encoding='utf-8')
self.html_formatter = HtmlFormatter(encoding="utf-8")
self.cpp_lexer = CppLexer(stripnl=False)
def render_source_lines(self, stream, line_remarks):
@ -74,31 +88,35 @@ class SourceFileRenderer:
if self.no_highlight:
html_highlighted = file_text
else:
html_highlighted = highlight(
file_text,
self.cpp_lexer,
self.html_formatter)
html_highlighted = highlight(file_text, self.cpp_lexer, self.html_formatter)
# Note that the API is different between Python 2 and 3. On
# Python 3, pygments.highlight() returns a bytes object, so we
# have to decode. On Python 2, the output is str but since we
# support unicode characters and the output streams is unicode we
# decode too.
html_highlighted = html_highlighted.decode('utf-8')
html_highlighted = html_highlighted.decode("utf-8")
# Take off the header and footer, these must be
# reapplied line-wise, within the page structure
html_highlighted = html_highlighted.replace('<div class="highlight"><pre>', '')
html_highlighted = html_highlighted.replace('</pre></div>', '')
html_highlighted = html_highlighted.replace(
'<div class="highlight"><pre>', ""
)
html_highlighted = html_highlighted.replace("</pre></div>", "")
for (linenum, html_line) in enumerate(html_highlighted.split('\n'), start=1):
print(u'''
for (linenum, html_line) in enumerate(html_highlighted.split("\n"), start=1):
print(
"""
<tr>
<td><a name=\"L{linenum}\">{linenum}</a></td>
<td></td>
<td></td>
<td><div class="highlight"><pre>{html_line}</pre></div></td>
</tr>'''.format(**locals()), file=self.stream)
</tr>""".format(
**locals()
),
file=self.stream,
)
for remark in line_remarks.get(linenum, []):
if not suppress(remark):
@ -109,42 +127,52 @@ class SourceFileRenderer:
dl = context.caller_loc.get(r.Function)
if dl:
dl_dict = dict(list(dl))
link = optrecord.make_link(dl_dict['File'], dl_dict['Line'] - 2)
inlining_context = "<a href={link}>{r.DemangledFunctionName}</a>".format(**locals())
link = optrecord.make_link(dl_dict["File"], dl_dict["Line"] - 2)
inlining_context = "<a href={link}>{r.DemangledFunctionName}</a>".format(
**locals()
)
# Column is the number of characters *including* tabs, keep those and
# replace everything else with spaces.
indent = line[:max(r.Column, 1) - 1]
indent = re.sub('\S', ' ', indent)
indent = line[: max(r.Column, 1) - 1]
indent = re.sub("\S", " ", indent)
# Create expanded message and link if we have a multiline message.
lines = r.message.split('\n')
lines = r.message.split("\n")
if len(lines) > 1:
expand_link = '<a style="text-decoration: none;" href="" onclick="toggleExpandedMessage(this); return false;">+</a>'
message = lines[0]
expand_message = u'''
expand_message = """
<div class="full-info" style="display:none;">
<div class="col-left"><pre style="display:inline">{}</pre></div>
<div class="expanded col-left"><pre>{}</pre></div>
</div>'''.format(indent, '\n'.join(lines[1:]))
</div>""".format(
indent, "\n".join(lines[1:])
)
else:
expand_link = ''
expand_message = ''
expand_link = ""
expand_message = ""
message = r.message
print(u'''
print(
"""
<tr>
<td></td>
<td>{r.RelativeHotness}</td>
<td class=\"column-entry-{r.color}\">{r.PassWithDiffPrefix}</td>
<td><pre style="display:inline">{indent}</pre><span class=\"column-entry-yellow\">{expand_link} {message}&nbsp;</span>{expand_message}</td>
<td class=\"column-entry-yellow\">{inlining_context}</td>
</tr>'''.format(**locals()), file=self.stream)
</tr>""".format(
**locals()
),
file=self.stream,
)
def render(self, line_remarks):
if not self.source_stream:
return
print(u'''
print(
"""
<html>
<title>{}</title>
<meta charset="utf-8" />
@ -180,34 +208,51 @@ function toggleExpandedMessage(e) {{
<th style="width: 15%">Inline Context</td>
</tr>
</thead>
<tbody>'''.format(os.path.basename(self.filename)), file=self.stream)
<tbody>""".format(
os.path.basename(self.filename)
),
file=self.stream,
)
self.render_source_lines(self.source_stream, line_remarks)
print(u'''
print(
"""
</tbody>
</table>
</body>
</html>''', file=self.stream)
</html>""",
file=self.stream,
)
class IndexRenderer:
def __init__(self, output_dir, should_display_hotness, max_hottest_remarks_on_index):
self.stream = io.open(os.path.join(output_dir, 'index.html'), 'w', encoding='utf-8')
def __init__(
self, output_dir, should_display_hotness, max_hottest_remarks_on_index
):
self.stream = io.open(
os.path.join(output_dir, "index.html"), "w", encoding="utf-8"
)
self.should_display_hotness = should_display_hotness
self.max_hottest_remarks_on_index = max_hottest_remarks_on_index
def render_entry(self, r, odd):
escaped_name = html.escape(r.DemangledFunctionName)
print(u'''
print(
"""
<tr>
<td class=\"column-entry-{odd}\"><a href={r.Link}>{r.DebugLocString}</a></td>
<td class=\"column-entry-{odd}\">{r.RelativeHotness}</td>
<td class=\"column-entry-{odd}\">{escaped_name}</td>
<td class=\"column-entry-{r.color}\">{r.PassWithDiffPrefix}</td>
</tr>'''.format(**locals()), file=self.stream)
</tr>""".format(
**locals()
),
file=self.stream,
)
def render(self, all_remarks):
print(u'''
print(
"""
<html>
<meta charset="utf-8" />
<head>
@ -221,7 +266,9 @@ class IndexRenderer:
<td>Hotness</td>
<td>Function</td>
<td>Pass</td>
</tr>''', file=self.stream)
</tr>""",
file=self.stream,
)
max_entries = None
if self.should_display_hotness:
@ -230,10 +277,13 @@ class IndexRenderer:
for i, remark in enumerate(all_remarks[:max_entries]):
if not suppress(remark):
self.render_entry(remark, i % 2)
print(u'''
print(
"""
</table>
</body>
</html>''', file=self.stream)
</html>""",
file=self.stream,
)
def _render_file(source_dir, output_dir, ctx, no_highlight, entry, filter_):
@ -247,26 +297,32 @@ def map_remarks(all_remarks):
# Set up a map between function names and their source location for
# function where inlining happened
for remark in optrecord.itervalues(all_remarks):
if isinstance(remark, optrecord.Passed) and remark.Pass == "inline" and remark.Name == "Inlined":
if (
isinstance(remark, optrecord.Passed)
and remark.Pass == "inline"
and remark.Name == "Inlined"
):
for arg in remark.Args:
arg_dict = dict(list(arg))
caller = arg_dict.get('Caller')
caller = arg_dict.get("Caller")
if caller:
try:
context.caller_loc[caller] = arg_dict['DebugLoc']
context.caller_loc[caller] = arg_dict["DebugLoc"]
except KeyError:
pass
def generate_report(all_remarks,
file_remarks,
source_dir,
output_dir,
no_highlight,
should_display_hotness,
max_hottest_remarks_on_index,
num_jobs,
should_print_progress):
def generate_report(
all_remarks,
file_remarks,
source_dir,
output_dir,
no_highlight,
should_display_hotness,
max_hottest_remarks_on_index,
num_jobs,
should_print_progress,
):
try:
os.makedirs(output_dir)
except OSError as e:
@ -276,75 +332,107 @@ def generate_report(all_remarks,
raise
if should_print_progress:
print('Rendering index page...')
print("Rendering index page...")
if should_display_hotness:
sorted_remarks = sorted(optrecord.itervalues(all_remarks), key=lambda r: (r.Hotness, r.File, r.Line, r.Column, r.PassWithDiffPrefix, r.yaml_tag, r.Function), reverse=True)
sorted_remarks = sorted(
optrecord.itervalues(all_remarks),
key=lambda r: (
r.Hotness,
r.File,
r.Line,
r.Column,
r.PassWithDiffPrefix,
r.yaml_tag,
r.Function,
),
reverse=True,
)
else:
sorted_remarks = sorted(optrecord.itervalues(all_remarks), key=lambda r: (r.File, r.Line, r.Column, r.PassWithDiffPrefix, r.yaml_tag, r.Function))
IndexRenderer(output_dir, should_display_hotness, max_hottest_remarks_on_index).render(sorted_remarks)
sorted_remarks = sorted(
optrecord.itervalues(all_remarks),
key=lambda r: (
r.File,
r.Line,
r.Column,
r.PassWithDiffPrefix,
r.yaml_tag,
r.Function,
),
)
IndexRenderer(
output_dir, should_display_hotness, max_hottest_remarks_on_index
).render(sorted_remarks)
shutil.copy(os.path.join(os.path.dirname(os.path.realpath(__file__)),
"style.css"), output_dir)
shutil.copy(
os.path.join(os.path.dirname(os.path.realpath(__file__)), "style.css"),
output_dir,
)
_render_file_bound = functools.partial(_render_file, source_dir, output_dir, context, no_highlight)
_render_file_bound = functools.partial(
_render_file, source_dir, output_dir, context, no_highlight
)
if should_print_progress:
print('Rendering HTML files...')
optpmap.pmap(_render_file_bound,
file_remarks.items(),
num_jobs,
should_print_progress)
print("Rendering HTML files...")
optpmap.pmap(
_render_file_bound, file_remarks.items(), num_jobs, should_print_progress
)
def main():
parser = argparse.ArgumentParser(description=desc)
parser.add_argument(
'yaml_dirs_or_files',
nargs='+',
help='List of optimization record files or directories searched '
'for optimization record files.')
"yaml_dirs_or_files",
nargs="+",
help="List of optimization record files or directories searched "
"for optimization record files.",
)
parser.add_argument(
'--output-dir',
'-o',
default='html',
help='Path to a directory where generated HTML files will be output. '
'If the directory does not already exist, it will be created. '
'"%(default)s" by default.')
"--output-dir",
"-o",
default="html",
help="Path to a directory where generated HTML files will be output. "
"If the directory does not already exist, it will be created. "
'"%(default)s" by default.',
)
parser.add_argument(
'--jobs',
'-j',
"--jobs",
"-j",
default=None,
type=int,
help='Max job count (defaults to %(default)s, the current CPU count)')
help="Max job count (defaults to %(default)s, the current CPU count)",
)
parser.add_argument("--source-dir", "-s", default="", help="set source directory")
parser.add_argument(
'--source-dir',
'-s',
default='',
help='set source directory')
parser.add_argument(
'--no-progress-indicator',
'-n',
action='store_true',
"--no-progress-indicator",
"-n",
action="store_true",
default=False,
help='Do not display any indicator of how many YAML files were read '
'or rendered into HTML.')
help="Do not display any indicator of how many YAML files were read "
"or rendered into HTML.",
)
parser.add_argument(
'--max-hottest-remarks-on-index',
"--max-hottest-remarks-on-index",
default=1000,
type=int,
help='Maximum number of the hottest remarks to appear on the index page')
help="Maximum number of the hottest remarks to appear on the index page",
)
parser.add_argument(
'--no-highlight',
action='store_true',
"--no-highlight",
action="store_true",
default=False,
help='Do not use a syntax highlighter when rendering the source code')
help="Do not use a syntax highlighter when rendering the source code",
)
parser.add_argument(
'--demangler',
help='Set the demangler to be used (defaults to %s)' % optrecord.Remark.default_demangler)
"--demangler",
help="Set the demangler to be used (defaults to %s)"
% optrecord.Remark.default_demangler,
)
parser.add_argument(
'--filter',
default='',
help='Only display remarks from passes matching filter expression')
"--filter",
default="",
help="Only display remarks from passes matching filter expression",
)
# Do not make this a global variable. Values needed to be propagated through
# to individual classes and functions to be portable with multiprocessing across
@ -360,20 +448,24 @@ def main():
parser.error("No *.opt.yaml files found")
sys.exit(1)
all_remarks, file_remarks, should_display_hotness = \
optrecord.gather_results(files, args.jobs, print_progress, args.filter)
all_remarks, file_remarks, should_display_hotness = optrecord.gather_results(
files, args.jobs, print_progress, args.filter
)
map_remarks(all_remarks)
generate_report(all_remarks,
file_remarks,
args.source_dir,
args.output_dir,
args.no_highlight,
should_display_hotness,
args.max_hottest_remarks_on_index,
args.jobs,
print_progress)
generate_report(
all_remarks,
file_remarks,
args.source_dir,
args.output_dir,
args.no_highlight,
should_display_hotness,
args.max_hottest_remarks_on_index,
args.jobs,
print_progress,
)
if __name__ == '__main__':
if __name__ == "__main__":
main()

View File

@ -19,13 +19,15 @@ def _wrapped_func(func_and_args):
if should_print_progress:
with _current.get_lock():
_current.value += 1
sys.stdout.write('\r\t{} of {}'.format(_current.value, _total.value))
sys.stdout.write("\r\t{} of {}".format(_current.value, _total.value))
sys.stdout.flush()
return func(argument, filter_)
def pmap(func, iterable, processes, should_print_progress, filter_=None, *args, **kwargs):
def pmap(
func, iterable, processes, should_print_progress, filter_=None, *args, **kwargs
):
"""
A parallel map function that reports on its progress.
@ -37,20 +39,25 @@ def pmap(func, iterable, processes, should_print_progress, filter_=None, *args,
"""
global _current
global _total
_current = multiprocessing.Value('i', 0)
_total = multiprocessing.Value('i', len(iterable))
_current = multiprocessing.Value("i", 0)
_total = multiprocessing.Value("i", len(iterable))
func_and_args = [(func, arg, should_print_progress, filter_) for arg in iterable]
if processes == 1:
result = list(map(_wrapped_func, func_and_args, *args, **kwargs))
else:
pool = multiprocessing.Pool(initializer=_init,
initargs=(_current, _total,),
processes=processes)
pool = multiprocessing.Pool(
initializer=_init,
initargs=(
_current,
_total,
),
processes=processes,
)
result = pool.map(_wrapped_func, func_and_args, *args, **kwargs)
pool.close()
pool.join()
if should_print_progress:
sys.stdout.write('\r')
sys.stdout.write("\r")
return result

View File

@ -4,6 +4,7 @@ from __future__ import print_function
import io
import yaml
# Try to use the C parser.
try:
from yaml import CLoader as Loader
@ -18,6 +19,7 @@ import functools
from multiprocessing import Lock
import os, os.path
import subprocess
try:
# The previously builtin function `intern()` was moved
# to the `sys` module in Python 3.
@ -35,42 +37,47 @@ except AttributeError:
# Python 3
def itervalues(d):
return iter(d.values())
def iteritems(d):
return iter(d.items())
else:
# Python 2
def itervalues(d):
return d.itervalues()
def iteritems(d):
return d.iteritems()
def html_file_name(filename):
return filename.replace('/', '_').replace('#', '_') + ".html"
return filename.replace("/", "_").replace("#", "_") + ".html"
def make_link(File, Line):
return "\"{}#L{}\"".format(html_file_name(File), Line)
return '"{}#L{}"'.format(html_file_name(File), Line)
class Remark(yaml.YAMLObject):
# Work-around for http://pyyaml.org/ticket/154.
yaml_loader = Loader
default_demangler = 'c++filt -n'
default_demangler = "c++filt -n"
demangler_proc = None
@classmethod
def set_demangler(cls, demangler):
cls.demangler_proc = subprocess.Popen(demangler.split(), stdin=subprocess.PIPE, stdout=subprocess.PIPE)
cls.demangler_proc = subprocess.Popen(
demangler.split(), stdin=subprocess.PIPE, stdout=subprocess.PIPE
)
cls.demangler_lock = Lock()
@classmethod
def demangle(cls, name):
with cls.demangler_lock:
cls.demangler_proc.stdin.write((name + '\n').encode('utf-8'))
cls.demangler_proc.stdin.write((name + "\n").encode("utf-8"))
cls.demangler_proc.stdin.flush()
return cls.demangler_proc.stdout.readline().rstrip().decode('utf-8')
return cls.demangler_proc.stdout.readline().rstrip().decode("utf-8")
# Intern all strings since we have lot of duplication across filenames,
# remark text.
@ -119,23 +126,23 @@ class Remark(yaml.YAMLObject):
self.Args = [tuple_to_dict(arg_tuple) for arg_tuple in self.Args]
def canonicalize(self):
if not hasattr(self, 'Hotness'):
if not hasattr(self, "Hotness"):
self.Hotness = 0
if not hasattr(self, 'Args'):
if not hasattr(self, "Args"):
self.Args = []
self._reduce_memory()
@property
def File(self):
return self.DebugLoc['File']
return self.DebugLoc["File"]
@property
def Line(self):
return int(self.DebugLoc['Line'])
return int(self.DebugLoc["Line"])
@property
def Column(self):
return self.DebugLoc['Column']
return self.DebugLoc["Column"]
@property
def DebugLocString(self):
@ -151,20 +158,21 @@ class Remark(yaml.YAMLObject):
def getArgString(self, mapping):
mapping = dict(list(mapping))
dl = mapping.get('DebugLoc')
dl = mapping.get("DebugLoc")
if dl:
del mapping['DebugLoc']
del mapping["DebugLoc"]
assert(len(mapping) == 1)
assert len(mapping) == 1
(key, value) = list(mapping.items())[0]
if key == 'Caller' or key == 'Callee' or key == 'DirectCallee':
if key == "Caller" or key == "Callee" or key == "DirectCallee":
value = html.escape(self.demangle(value))
if dl and key != 'Caller':
if dl and key != "Caller":
dl_dict = dict(list(dl))
return u"<a href={}>{}</a>".format(
make_link(dl_dict['File'], dl_dict['Line']), value)
return "<a href={}>{}</a>".format(
make_link(dl_dict["File"], dl_dict["Line"]), value
)
else:
return value
@ -173,15 +181,15 @@ class Remark(yaml.YAMLObject):
# list containing the value (e.g. for 'Callee' the function) and
# optionally a DebugLoc.
def getArgDict(self):
if hasattr(self, 'ArgDict'):
if hasattr(self, "ArgDict"):
return self.ArgDict
self.ArgDict = {}
for arg in self.Args:
if len(arg) == 2:
if arg[0][0] == 'DebugLoc':
if arg[0][0] == "DebugLoc":
dbgidx = 0
else:
assert(arg[1][0] == 'DebugLoc')
assert arg[1][0] == "DebugLoc"
dbgidx = 1
key = arg[1 - dbgidx][0]
@ -189,18 +197,18 @@ class Remark(yaml.YAMLObject):
else:
arg = arg[0]
key = arg[0]
entry = (arg[1], )
entry = (arg[1],)
self.ArgDict[key] = entry
return self.ArgDict
def getDiffPrefix(self):
if hasattr(self, 'Added'):
if hasattr(self, "Added"):
if self.Added:
return '+'
return "+"
else:
return '-'
return ''
return "-"
return ""
@property
def PassWithDiffPrefix(self):
@ -215,14 +223,22 @@ class Remark(yaml.YAMLObject):
@property
def RelativeHotness(self):
if self.max_hotness:
return "{0:.2f}%".format(self.Hotness * 100. / self.max_hotness)
return "{0:.2f}%".format(self.Hotness * 100.0 / self.max_hotness)
else:
return ''
return ""
@property
def key(self):
return (self.__class__, self.PassWithDiffPrefix, self.Name, self.File,
self.Line, self.Column, self.Function, self.Args)
return (
self.__class__,
self.PassWithDiffPrefix,
self.Name,
self.File,
self.Line,
self.Column,
self.Function,
self.Args,
)
def __hash__(self):
return hash(self.key)
@ -235,7 +251,7 @@ class Remark(yaml.YAMLObject):
class Analysis(Remark):
yaml_tag = '!Analysis'
yaml_tag = "!Analysis"
@property
def color(self):
@ -243,15 +259,15 @@ class Analysis(Remark):
class AnalysisFPCommute(Analysis):
yaml_tag = '!AnalysisFPCommute'
yaml_tag = "!AnalysisFPCommute"
class AnalysisAliasing(Analysis):
yaml_tag = '!AnalysisAliasing'
yaml_tag = "!AnalysisAliasing"
class Passed(Remark):
yaml_tag = '!Passed'
yaml_tag = "!Passed"
@property
def color(self):
@ -259,21 +275,23 @@ class Passed(Remark):
class Missed(Remark):
yaml_tag = '!Missed'
yaml_tag = "!Missed"
@property
def color(self):
return "red"
class Failure(Missed):
yaml_tag = '!Failure'
yaml_tag = "!Failure"
def get_remarks(input_file, filter_=None):
max_hotness = 0
all_remarks = dict()
file_remarks = defaultdict(functools.partial(defaultdict, list))
with io.open(input_file, encoding = 'utf-8') as f:
with io.open(input_file, encoding="utf-8") as f:
docs = yaml.load_all(f, Loader=Loader)
filter_e = None
@ -282,7 +300,7 @@ def get_remarks(input_file, filter_=None):
for remark in docs:
remark.canonicalize()
# Avoid remarks withoug debug location or if they are duplicated
if not hasattr(remark, 'DebugLoc') or remark.key in all_remarks:
if not hasattr(remark, "DebugLoc") or remark.key in all_remarks:
continue
if filter_e and not filter_e.search(remark.Pass):
@ -295,7 +313,7 @@ def get_remarks(input_file, filter_=None):
# If we're reading a back a diff yaml file, max_hotness is already
# captured which may actually be less than the max hotness found
# in the file.
if hasattr(remark, 'max_hotness'):
if hasattr(remark, "max_hotness"):
max_hotness = remark.max_hotness
max_hotness = max(max_hotness, remark.Hotness)
@ -304,11 +322,12 @@ def get_remarks(input_file, filter_=None):
def gather_results(filenames, num_jobs, should_print_progress, filter_=None):
if should_print_progress:
print('Reading YAML files...')
print("Reading YAML files...")
if not Remark.demangler_proc:
Remark.set_demangler(Remark.default_demangler)
remarks = optpmap.pmap(
get_remarks, filenames, num_jobs, should_print_progress, filter_)
get_remarks, filenames, num_jobs, should_print_progress, filter_
)
max_hotness = max(entry[0] for entry in remarks)
def merge_file_remarks(file_remarks_job, all_remarks, merged):
@ -338,8 +357,9 @@ def find_opt_files(*dirs_or_files):
else:
for dir, subdirs, files in os.walk(dir_or_file):
# Exclude mounted directories and symlinks (os.walk default).
subdirs[:] = [d for d in subdirs
if not os.path.ismount(os.path.join(dir, d))]
subdirs[:] = [
d for d in subdirs if not os.path.ismount(os.path.join(dir, d))
]
for file in files:
if fnmatch.fnmatch(file, "*.opt.yaml*"):
all.append(os.path.join(dir, file))

View File

@ -1,12 +1,12 @@
#!/usr/bin/env python3
#===- symcov-report-server.py - Coverage Reports HTTP Serve --*- python -*--===#
# ===- symcov-report-server.py - Coverage Reports HTTP Serve --*- python -*--===#
#
# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
# See https://llvm.org/LICENSE.txt for license information.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#
#===------------------------------------------------------------------------===#
'''(EXPERIMENTAL) HTTP server to browse coverage reports from .symcov files.
# ===------------------------------------------------------------------------===#
"""(EXPERIMENTAL) HTTP server to browse coverage reports from .symcov files.
Coverage reports for big binaries are too huge, generating them statically
makes no sense. Start the server and go to localhost:8001 instead.
@ -19,7 +19,7 @@ Usage:
Other options:
--port port_number - specifies the port to use (8001)
--host host_name - host name to bind server to (127.0.0.1)
'''
"""
from __future__ import print_function
@ -73,10 +73,11 @@ $content
FILE_URI_PREFIX = "/file/"
class SymcovData:
def __init__(self, symcov_json):
self.covered_points = frozenset(symcov_json['covered-points'])
self.point_symbol_info = symcov_json['point-symbol-info']
self.covered_points = frozenset(symcov_json["covered-points"])
self.point_symbol_info = symcov_json["point-symbol-info"]
self.file_coverage = self.compute_filecoverage()
def filenames(self):
@ -114,25 +115,29 @@ class SymcovData:
for fn, points in fns.items():
file_points.extend(points.keys())
covered_points = self.covered_points & set(file_points)
result[filename] = int(math.ceil(
len(covered_points) * 100 / len(file_points)))
result[filename] = int(
math.ceil(len(covered_points) * 100 / len(file_points))
)
return result
def format_pct(pct):
pct_str = str(max(0, min(100, pct)))
zeroes = '0' * (3 - len(pct_str))
zeroes = "0" * (3 - len(pct_str))
if zeroes:
zeroes = '<span class="lz">{0}</span>'.format(zeroes)
return zeroes + pct_str
class ServerHandler(http.server.BaseHTTPRequestHandler):
symcov_data = None
src_path = None
def do_GET(self):
norm_path = os.path.normpath(urllib.parse.unquote(self.path[len(FILE_URI_PREFIX):]))
if self.path == '/':
norm_path = os.path.normpath(
urllib.parse.unquote(self.path[len(FILE_URI_PREFIX) :])
)
if self.path == "/":
self.send_response(200)
self.send_header("Content-type", "text/html; charset=utf-8")
self.end_headers()
@ -143,18 +148,21 @@ class ServerHandler(http.server.BaseHTTPRequestHandler):
if not file_coverage:
continue
filelist.append(
"<tr><td><a href=\"{prefix}{name}\">{name}</a></td>"
"<td>{coverage}%</td></tr>".format(
prefix=FILE_URI_PREFIX,
name=html.escape(filename, quote=True),
coverage=format_pct(file_coverage)))
'<tr><td><a href="{prefix}{name}">{name}</a></td>'
"<td>{coverage}%</td></tr>".format(
prefix=FILE_URI_PREFIX,
name=html.escape(filename, quote=True),
coverage=format_pct(file_coverage),
)
)
response = string.Template(INDEX_PAGE_TMPL).safe_substitute(
filenames='\n'.join(filelist))
self.wfile.write(response.encode('UTF-8', 'replace'))
filenames="\n".join(filelist)
)
self.wfile.write(response.encode("UTF-8", "replace"))
elif self.symcov_data.has_file(norm_path):
filename = norm_path
filepath = os.path.join(self.src_path, filename)
filepath = os.path.join(self.src_path, filename)
if not os.path.exists(filepath):
self.send_response(404)
self.end_headers()
@ -166,18 +174,22 @@ class ServerHandler(http.server.BaseHTTPRequestHandler):
linemap = self.symcov_data.compute_linemap(filename)
with open(filepath, 'r', encoding='utf8') as f:
with open(filepath, "r", encoding="utf8") as f:
content = "\n".join(
["<span class='{cls}'>{line}&nbsp;</span>".format(
line=html.escape(line.rstrip()),
cls=linemap.get(line_no, ""))
for line_no, line in enumerate(f, start=1)])
[
"<span class='{cls}'>{line}&nbsp;</span>".format(
line=html.escape(line.rstrip()),
cls=linemap.get(line_no, ""),
)
for line_no, line in enumerate(f, start=1)
]
)
response = string.Template(CONTENT_PAGE_TMPL).safe_substitute(
path=self.path[1:],
content=content)
path=self.path[1:], content=content
)
self.wfile.write(response.encode('UTF-8', 'replace'))
self.wfile.write(response.encode("UTF-8", "replace"))
else:
self.send_response(404)
self.end_headers()
@ -185,10 +197,10 @@ class ServerHandler(http.server.BaseHTTPRequestHandler):
def main():
parser = argparse.ArgumentParser(description="symcov report http server.")
parser.add_argument('--host', default='127.0.0.1')
parser.add_argument('--port', default=8001)
parser.add_argument('--symcov', required=True, type=argparse.FileType('r'))
parser.add_argument('--srcpath', required=True)
parser.add_argument("--host", default="127.0.0.1")
parser.add_argument("--port", default=8001)
parser.add_argument("--symcov", required=True, type=argparse.FileType("r"))
parser.add_argument("--srcpath", required=True)
args = parser.parse_args()
print("Loading coverage...")
@ -205,5 +217,6 @@ def main():
pass
httpd.server_close()
if __name__ == '__main__':
if __name__ == "__main__":
main()

View File

@ -1,35 +1,36 @@
#!/usr/bin/env python
#changelog:
#10/13/2005b: replaced the # in tmp(.#*)* with alphanumeric and _, this will then remove
#nodes such as %tmp.1.i and %tmp._i.3
#10/13/2005: exntended to remove variables of the form %tmp(.#)* rather than just
# changelog:
# 10/13/2005b: replaced the # in tmp(.#*)* with alphanumeric and _, this will then remove
# nodes such as %tmp.1.i and %tmp._i.3
# 10/13/2005: exntended to remove variables of the form %tmp(.#)* rather than just
#%tmp.#, i.e. it now will remove %tmp.12.3.15 etc, additionally fixed a spelling error in
#the comments
#10/12/2005: now it only removes nodes and edges for which the label is %tmp.# rather
#than removing all lines for which the lable CONTAINS %tmp.#
# the comments
# 10/12/2005: now it only removes nodes and edges for which the label is %tmp.# rather
# than removing all lines for which the lable CONTAINS %tmp.#
from __future__ import print_function
import re
import sys
if( len(sys.argv) < 3 ):
print('usage is: ./DSAclean <dot_file_to_be_cleaned> <out_put_file>')
sys.exit(1)
#get a file object
input = open(sys.argv[1], 'r')
output = open(sys.argv[2], 'w')
#we'll get this one line at a time...while we could just put the whole thing in a string
#it would kill old computers
if len(sys.argv) < 3:
print("usage is: ./DSAclean <dot_file_to_be_cleaned> <out_put_file>")
sys.exit(1)
# get a file object
input = open(sys.argv[1], "r")
output = open(sys.argv[2], "w")
# we'll get this one line at a time...while we could just put the whole thing in a string
# it would kill old computers
buffer = input.readline()
while buffer != '':
if re.compile("label(\s*)=(\s*)\"\s%tmp(.\w*)*(\s*)\"").search(buffer):
#skip next line, write neither this line nor the next
buffer = input.readline()
else:
#this isn't a tmp Node, we can write it
output.write(buffer)
#prepare for the next iteration
buffer = input.readline()
while buffer != "":
if re.compile('label(\s*)=(\s*)"\s%tmp(.\w*)*(\s*)"').search(buffer):
# skip next line, write neither this line nor the next
buffer = input.readline()
else:
# this isn't a tmp Node, we can write it
output.write(buffer)
# prepare for the next iteration
buffer = input.readline()
input.close()
output.close()

View File

@ -1,29 +1,29 @@
#!/usr/bin/env python
#this is a script to extract given named nodes from a dot file, with
#the associated edges. An edge is kept iff for edge x -> y
# this is a script to extract given named nodes from a dot file, with
# the associated edges. An edge is kept iff for edge x -> y
# x and y are both nodes specified to be kept.
#known issues: if a line contains '->' and is not an edge line
#problems will occur. If node labels do not begin with
#Node this also will not work. Since this is designed to work
#on DSA dot output and not general dot files this is ok.
#If you want to use this on other files rename the node labels
#to Node[.*] with a script or something. This also relies on
#the length of a node name being 13 characters (as it is in all
#DSA dot output files)
# known issues: if a line contains '->' and is not an edge line
# problems will occur. If node labels do not begin with
# Node this also will not work. Since this is designed to work
# on DSA dot output and not general dot files this is ok.
# If you want to use this on other files rename the node labels
# to Node[.*] with a script or something. This also relies on
# the length of a node name being 13 characters (as it is in all
# DSA dot output files)
#Note that the name of the node can be any substring of the actual
#name in the dot file. Thus if you say specify COLLAPSED
#as a parameter this script will pull out all COLLAPSED
#nodes in the file
# Note that the name of the node can be any substring of the actual
# name in the dot file. Thus if you say specify COLLAPSED
# as a parameter this script will pull out all COLLAPSED
# nodes in the file
#Specifying escape characters in the name like \n also will not work,
#as Python
#will make it \\n, I'm not really sure how to fix this
# Specifying escape characters in the name like \n also will not work,
# as Python
# will make it \\n, I'm not really sure how to fix this
#currently the script prints the names it is searching for
#to STDOUT, so you can check to see if they are what you intend
# currently the script prints the names it is searching for
# to STDOUT, so you can check to see if they are what you intend
from __future__ import print_function
@ -33,81 +33,81 @@ import sys
if len(sys.argv) < 3:
print('usage is ./DSAextract <dot_file_to_modify> \
<output_file> [list of nodes to extract]')
print(
"usage is ./DSAextract <dot_file_to_modify> \
<output_file> [list of nodes to extract]"
)
#open the input file
input = open(sys.argv[1], 'r')
# open the input file
input = open(sys.argv[1], "r")
#construct a set of node names
# construct a set of node names
node_name_set = set()
for name in sys.argv[3:]:
node_name_set |= set([name])
node_name_set |= set([name])
#construct a list of compiled regular expressions from the
#node_name_set
# construct a list of compiled regular expressions from the
# node_name_set
regexp_list = []
for name in node_name_set:
regexp_list.append(re.compile(name))
regexp_list.append(re.compile(name))
#used to see what kind of line we are on
nodeexp = re.compile('Node')
#used to check to see if the current line is an edge line
arrowexp = re.compile('->')
# used to see what kind of line we are on
nodeexp = re.compile("Node")
# used to check to see if the current line is an edge line
arrowexp = re.compile("->")
node_set = set()
#read the file one line at a time
# read the file one line at a time
buffer = input.readline()
while buffer != '':
#filter out the unnecessary checks on all the edge lines
if not arrowexp.search(buffer):
#check to see if this is a node we are looking for
for regexp in regexp_list:
#if this name is for the current node, add the dot variable name
#for the node (it will be Node(hex number)) to our set of nodes
if regexp.search(buffer):
node_set |= set([re.split('\s+',buffer,2)[1]])
break
buffer = input.readline()
while buffer != "":
# filter out the unnecessary checks on all the edge lines
if not arrowexp.search(buffer):
# check to see if this is a node we are looking for
for regexp in regexp_list:
# if this name is for the current node, add the dot variable name
# for the node (it will be Node(hex number)) to our set of nodes
if regexp.search(buffer):
node_set |= set([re.split("\s+", buffer, 2)[1]])
break
buffer = input.readline()
#test code
#print '\n'
# test code
# print '\n'
print(node_name_set)
#print node_set
# print node_set
#open the output file
output = open(sys.argv[2], 'w')
#start the second pass over the file
input = open(sys.argv[1], 'r')
# open the output file
output = open(sys.argv[2], "w")
# start the second pass over the file
input = open(sys.argv[1], "r")
buffer = input.readline()
while buffer != '':
#there are three types of lines we are looking for
#1) node lines, 2) edge lines 3) support lines (like page size, etc)
#is this an edge line?
#note that this is no completely robust, if a none edge line
#for some reason contains -> it will be missidentified
#hand edit the file if this happens
if arrowexp.search(buffer):
#check to make sure that both nodes are in the node list
#if they are print this to output
nodes = arrowexp.split(buffer)
nodes[0] = string.strip(nodes[0])
nodes[1] = string.strip(nodes[1])
if nodes[0][:13] in node_set and \
nodes[1][:13] in node_set:
output.write(buffer)
elif nodeexp.search(buffer): #this is a node line
node = re.split('\s+', buffer,2)[1]
if node in node_set:
output.write(buffer)
else: #this is a support line
output.write(buffer)
buffer = input.readline()
while buffer != "":
# there are three types of lines we are looking for
# 1) node lines, 2) edge lines 3) support lines (like page size, etc)
# is this an edge line?
# note that this is no completely robust, if a none edge line
# for some reason contains -> it will be missidentified
# hand edit the file if this happens
if arrowexp.search(buffer):
# check to make sure that both nodes are in the node list
# if they are print this to output
nodes = arrowexp.split(buffer)
nodes[0] = string.strip(nodes[0])
nodes[1] = string.strip(nodes[1])
if nodes[0][:13] in node_set and nodes[1][:13] in node_set:
output.write(buffer)
elif nodeexp.search(buffer): # this is a node line
node = re.split("\s+", buffer, 2)[1]
if node in node_set:
output.write(buffer)
else: # this is a support line
output.write(buffer)
buffer = input.readline()

View File

@ -21,8 +21,7 @@ from phabricator import Phabricator
# $ . ./venv/bin/activate
# $ pip install Phabricator
GIT_REPO_METADATA = (("llvm-monorepo", "https://github.com/llvm/llvm-project"),
)
GIT_REPO_METADATA = (("llvm-monorepo", "https://github.com/llvm/llvm-project"),)
# The below PhabXXX classes represent objects as modelled by Phabricator.
# The classes can be serialized to disk, to try and make sure that we don't
@ -72,25 +71,30 @@ class PhabObjectCache:
try:
f = open(self._get_pickle_name(directory), "rb")
except IOError as err:
print("Could not find cache. Error message: {0}. Continuing..."
.format(err))
print("Could not find cache. Error message: {0}. Continuing...".format(err))
else:
with f:
try:
d = pickle.load(f)
self.__dict__.update(d)
except EOFError as err:
print("Cache seems to be corrupt. " +
"Not using cache. Error message: {0}".format(err))
print(
"Cache seems to be corrupt. "
+ "Not using cache. Error message: {0}".format(err)
)
def write_cache_to_disk(self, directory=DEFAULT_DIRECTORY):
if not os.path.exists(directory):
os.makedirs(directory)
with open(self._get_pickle_name(directory), "wb") as f:
pickle.dump(self.__dict__, f)
print("wrote cache to disk, most_recent_info= {0}".format(
datetime.fromtimestamp(self.most_recent_info)
if self.most_recent_info is not None else None))
print(
"wrote cache to disk, most_recent_info= {0}".format(
datetime.fromtimestamp(self.most_recent_info)
if self.most_recent_info is not None
else None
)
)
class PhabReview(PhabObject):
@ -162,8 +166,9 @@ class PhabHunk:
# Merge the adjacent and overlapping ranges in there:
t = []
lastRange = None
for start, end in self.actual_lines_changed_offset + \
[(sys.maxsize, sys.maxsize)]:
for start, end in self.actual_lines_changed_offset + [
(sys.maxsize, sys.maxsize)
]:
if lastRange is None:
lastRange = (start, end)
else:
@ -214,48 +219,64 @@ def init_phab_connection():
return phab
def update_cached_info(phab, cache, phab_query, order, record_results,
max_nr_entries_per_fetch, max_nr_days_to_cache):
def update_cached_info(
phab,
cache,
phab_query,
order,
record_results,
max_nr_entries_per_fetch,
max_nr_days_to_cache,
):
q = phab
LIMIT = max_nr_entries_per_fetch
for query_step in phab_query:
q = getattr(q, query_step)
results = q(order=order, limit=LIMIT)
most_recent_info, oldest_info = record_results(cache, results, phab)
oldest_info_to_fetch = datetime.fromtimestamp(most_recent_info) - \
timedelta(days=max_nr_days_to_cache)
oldest_info_to_fetch = datetime.fromtimestamp(most_recent_info) - timedelta(
days=max_nr_days_to_cache
)
most_recent_info_overall = most_recent_info
cache.write_cache_to_disk()
after = results["cursor"]["after"]
print("after: {0!r}".format(after))
print("most_recent_info: {0}".format(
datetime.fromtimestamp(most_recent_info)))
while (after is not None
and datetime.fromtimestamp(oldest_info) > oldest_info_to_fetch):
need_more_older_data = \
(cache.oldest_info is None or
datetime.fromtimestamp(cache.oldest_info) > oldest_info_to_fetch)
print(("need_more_older_data={0} cache.oldest_info={1} " +
"oldest_info_to_fetch={2}").format(
need_more_older_data,
datetime.fromtimestamp(cache.oldest_info)
if cache.oldest_info is not None else None,
oldest_info_to_fetch))
need_more_newer_data = \
(cache.most_recent_info is None or
cache.most_recent_info < most_recent_info)
print(("need_more_newer_data={0} cache.most_recent_info={1} " +
"most_recent_info={2}")
.format(need_more_newer_data, cache.most_recent_info,
most_recent_info))
print("most_recent_info: {0}".format(datetime.fromtimestamp(most_recent_info)))
while (
after is not None and datetime.fromtimestamp(oldest_info) > oldest_info_to_fetch
):
need_more_older_data = (
cache.oldest_info is None
or datetime.fromtimestamp(cache.oldest_info) > oldest_info_to_fetch
)
print(
(
"need_more_older_data={0} cache.oldest_info={1} "
+ "oldest_info_to_fetch={2}"
).format(
need_more_older_data,
datetime.fromtimestamp(cache.oldest_info)
if cache.oldest_info is not None
else None,
oldest_info_to_fetch,
)
)
need_more_newer_data = (
cache.most_recent_info is None or cache.most_recent_info < most_recent_info
)
print(
(
"need_more_newer_data={0} cache.most_recent_info={1} "
+ "most_recent_info={2}"
).format(need_more_newer_data, cache.most_recent_info, most_recent_info)
)
if not need_more_older_data and not need_more_newer_data:
break
results = q(order=order, after=after, limit=LIMIT)
most_recent_info, oldest_info = record_results(cache, results, phab)
after = results["cursor"]["after"]
print("after: {0!r}".format(after))
print("most_recent_info: {0}".format(
datetime.fromtimestamp(most_recent_info)))
print("most_recent_info: {0}".format(datetime.fromtimestamp(most_recent_info)))
cache.write_cache_to_disk()
cache.most_recent_info = most_recent_info_overall
if after is None:
@ -279,8 +300,10 @@ def record_reviews(cache, reviews, phab):
title = reviewInfo["fields"]["title"]
author = reviewInfo["fields"]["authorPHID"]
phabReview = cache.get(id)
if "dateModified" not in phabReview.__dict__ or \
dateModified > phabReview.dateModified:
if (
"dateModified" not in phabReview.__dict__
or dateModified > phabReview.dateModified
):
diff_results = phab.differential.querydiffs(revisionIDs=[id])
diff_ids = sorted(diff_results.keys())
phabDiffs = []
@ -291,8 +314,11 @@ def record_reviews(cache, reviews, phab):
phabDiffs.append(d)
phabReview.update(title, dateCreated, dateModified, author)
phabReview.setPhabDiffs(phabDiffs)
print("Updated D{0} modified on {1} ({2} diffs)".format(
id, datetime.fromtimestamp(dateModified), len(phabDiffs)))
print(
"Updated D{0} modified on {1} ({2} diffs)".format(
id, datetime.fromtimestamp(dateModified), len(phabDiffs)
)
)
if most_recent_info is None:
most_recent_info = dateModified
@ -330,41 +356,66 @@ def record_users(cache, users, phab):
return most_recent_info, oldest_info
PHABCACHESINFO = ((reviews_cache, ("differential", "revision", "search"),
"updated", record_reviews, 5, 7),
(users_cache, ("user", "search"), "newest", record_users,
100, 1000))
PHABCACHESINFO = (
(
reviews_cache,
("differential", "revision", "search"),
"updated",
record_reviews,
5,
7,
),
(users_cache, ("user", "search"), "newest", record_users, 100, 1000),
)
def load_cache():
for cache, phab_query, order, record_results, _, _ in PHABCACHESINFO:
cache.populate_cache_from_disk()
print("Loaded {0} nr entries: {1}".format(
cache.get_name(), len(cache.get_ids_in_cache())))
print("Loaded {0} has most recent info: {1}".format(
cache.get_name(),
datetime.fromtimestamp(cache.most_recent_info)
if cache.most_recent_info is not None else None))
print(
"Loaded {0} nr entries: {1}".format(
cache.get_name(), len(cache.get_ids_in_cache())
)
)
print(
"Loaded {0} has most recent info: {1}".format(
cache.get_name(),
datetime.fromtimestamp(cache.most_recent_info)
if cache.most_recent_info is not None
else None,
)
)
def update_cache(phab):
load_cache()
for cache, phab_query, order, record_results, max_nr_entries_per_fetch, \
max_nr_days_to_cache in PHABCACHESINFO:
update_cached_info(phab, cache, phab_query, order, record_results,
max_nr_entries_per_fetch, max_nr_days_to_cache)
for (
cache,
phab_query,
order,
record_results,
max_nr_entries_per_fetch,
max_nr_days_to_cache,
) in PHABCACHESINFO:
update_cached_info(
phab,
cache,
phab_query,
order,
record_results,
max_nr_entries_per_fetch,
max_nr_days_to_cache,
)
ids_in_cache = cache.get_ids_in_cache()
print("{0} objects in {1}".format(len(ids_in_cache), cache.get_name()))
cache.write_cache_to_disk()
def get_most_recent_reviews(days):
newest_reviews = sorted(
reviews_cache.get_objects(), key=lambda r: -r.dateModified)
newest_reviews = sorted(reviews_cache.get_objects(), key=lambda r: -r.dateModified)
if len(newest_reviews) == 0:
return newest_reviews
most_recent_review_time = \
datetime.fromtimestamp(newest_reviews[0].dateModified)
most_recent_review_time = datetime.fromtimestamp(newest_reviews[0].dateModified)
cut_off_date = most_recent_review_time - timedelta(days=days)
result = []
for review in newest_reviews:
@ -395,36 +446,51 @@ def print_most_recent_reviews(phab, days, filter_reviewers):
def add_msg(msg):
msgs.append(msg)
print(msg.encode('utf-8'))
print(msg.encode("utf-8"))
newest_reviews = get_most_recent_reviews(days)
add_msg(u"These are the reviews that look interesting to be reviewed. " +
u"The report below has 2 sections. The first " +
u"section is organized per review; the second section is organized "
+ u"per potential reviewer.\n")
add_msg(
"These are the reviews that look interesting to be reviewed. "
+ "The report below has 2 sections. The first "
+ "section is organized per review; the second section is organized "
+ "per potential reviewer.\n"
)
oldest_review = newest_reviews[-1] if len(newest_reviews) > 0 else None
oldest_datetime = \
datetime.fromtimestamp(oldest_review.dateModified) \
if oldest_review else None
add_msg((u"The report below is based on analyzing the reviews that got " +
u"touched in the past {0} days (since {1}). " +
u"The script found {2} such reviews.\n").format(
days, oldest_datetime, len(newest_reviews)))
oldest_datetime = (
datetime.fromtimestamp(oldest_review.dateModified) if oldest_review else None
)
add_msg(
(
"The report below is based on analyzing the reviews that got "
+ "touched in the past {0} days (since {1}). "
+ "The script found {2} such reviews.\n"
).format(days, oldest_datetime, len(newest_reviews))
)
reviewer2reviews_and_scores = {}
for i, review in enumerate(newest_reviews):
matched_reviewers = find_reviewers_for_review(review)
matched_reviewers = filter_reviewers(matched_reviewers)
if len(matched_reviewers) == 0:
continue
add_msg((u"{0:>3}. https://reviews.llvm.org/D{1} by {2}\n {3}\n" +
u" Last updated on {4}").format(
i, review.id,
get_real_name_from_author(review.author), review.title,
datetime.fromtimestamp(review.dateModified)))
add_msg(
(
"{0:>3}. https://reviews.llvm.org/D{1} by {2}\n {3}\n"
+ " Last updated on {4}"
).format(
i,
review.id,
get_real_name_from_author(review.author),
review.title,
datetime.fromtimestamp(review.dateModified),
)
)
for reviewer, scores in matched_reviewers:
add_msg(u" potential reviewer {0}, score {1}".format(
reviewer,
"(" + "/".join(["{0:.1f}%".format(s) for s in scores]) + ")"))
add_msg(
" potential reviewer {0}, score {1}".format(
reviewer,
"(" + "/".join(["{0:.1f}%".format(s) for s in scores]) + ")",
)
)
if reviewer not in reviewer2reviews_and_scores:
reviewer2reviews_and_scores[reviewer] = []
reviewer2reviews_and_scores[reviewer].append((review, scores))
@ -433,12 +499,20 @@ def print_most_recent_reviews(phab, days, filter_reviewers):
for reviewer in sorted(reviewer2reviews_and_scores.keys()):
reviews_and_scores = reviewer2reviews_and_scores[reviewer]
reviews_and_scores.sort(key=lambda rs: rs[1], reverse=True)
add_msg(u"\n\nSUMMARY FOR {0} (found {1} reviews):".format(
reviewer, len(reviews_and_scores)))
add_msg(
"\n\nSUMMARY FOR {0} (found {1} reviews):".format(
reviewer, len(reviews_and_scores)
)
)
for review, scores in reviews_and_scores:
add_msg(u"[{0}] https://reviews.llvm.org/D{1} '{2}' by {3}".format(
"/".join(["{0:.1f}%".format(s) for s in scores]), review.id,
review.title, get_real_name_from_author(review.author)))
add_msg(
"[{0}] https://reviews.llvm.org/D{1} '{2}' by {3}".format(
"/".join(["{0:.1f}%".format(s) for s in scores]),
review.id,
review.title,
get_real_name_from_author(review.author),
)
)
return "\n".join(msgs)
@ -446,13 +520,12 @@ def get_git_cmd_output(cmd):
output = None
try:
logging.debug(cmd)
output = subprocess.check_output(
cmd, shell=True, stderr=subprocess.STDOUT)
output = subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e:
logging.debug(str(e))
if output is None:
return None
return output.decode("utf-8", errors='ignore')
return output.decode("utf-8", errors="ignore")
reAuthorMail = re.compile("^author-mail <([^>]*)>.*$")
@ -480,12 +553,14 @@ class BlameOutputCache:
def _populate_cache_for(self, cache_key):
assert cache_key not in self.cache
git_repo, base_revision, path = cache_key
cmd = ("git -C {0} blame --encoding=utf-8 --date iso -f -e -w " +
"--line-porcelain {1} -- {2}").format(git_repo, base_revision,
path)
cmd = (
"git -C {0} blame --encoding=utf-8 --date iso -f -e -w "
+ "--line-porcelain {1} -- {2}"
).format(git_repo, base_revision, path)
blame_output = get_git_cmd_output(cmd)
self.cache[cache_key] = \
blame_output.split('\n') if blame_output is not None else None
self.cache[cache_key] = (
blame_output.split("\n") if blame_output is not None else None
)
# FIXME: the blame cache could probably be made more effective still if
# instead of storing the requested base_revision in the cache, the last
# revision before the base revision this file/path got changed in gets
@ -493,8 +568,9 @@ class BlameOutputCache:
# file/patch hasn't changed would get cache hits (instead of misses in
# the current implementation).
def get_blame_output_for(self, git_repo, base_revision, path, start_line=-1,
end_line=-1):
def get_blame_output_for(
self, git_repo, base_revision, path, start_line=-1, end_line=-1
):
cache_key = (git_repo, base_revision, path)
if cache_key not in self.cache:
self._populate_cache_for(cache_key)
@ -511,11 +587,14 @@ class BlameOutputCache:
assert start_line <= end_line
return all_blame_lines[start_line:end_line]
def get_parsed_git_blame_for(self, git_repo, base_revision, path,
start_line=-1, end_line=-1):
def get_parsed_git_blame_for(
self, git_repo, base_revision, path, start_line=-1, end_line=-1
):
return parse_blame_output_line_porcelain(
self.get_blame_output_for(git_repo, base_revision, path, start_line,
end_line))
self.get_blame_output_for(
git_repo, base_revision, path, start_line, end_line
)
)
blameOutputCache = BlameOutputCache()
@ -534,8 +613,8 @@ def find_reviewers_for_diff_heuristic(diff):
git_repo = os.path.join("git_repos", GIT_REPO_METADATA[0][0])
cmd = 'git -C {0} rev-list -n 1 --before="{1}" main'.format(
git_repo,
datetime.fromtimestamp(
diff.dateModified).strftime("%Y-%m-%d %H:%M:%s"))
datetime.fromtimestamp(diff.dateModified).strftime("%Y-%m-%d %H:%M:%s"),
)
base_revision = get_git_cmd_output(cmd).strip()
logging.debug("Base revision={0}".format(base_revision))
for change in diff.changes:
@ -544,18 +623,20 @@ def find_reviewers_for_diff_heuristic(diff):
for hunk in change.hunks:
for start_line, end_line in hunk.actual_lines_changed_offset:
# Collect git blame results for authors in those ranges.
for reviewer, nr_occurences in \
blameOutputCache.get_parsed_git_blame_for(
git_repo, base_revision, path, start_line, end_line
).items():
for (
reviewer,
nr_occurences,
) in blameOutputCache.get_parsed_git_blame_for(
git_repo, base_revision, path, start_line, end_line
).items():
if reviewer not in reviewers2nr_lines_touched:
reviewers2nr_lines_touched[reviewer] = 0
reviewers2nr_lines_touched[reviewer] += nr_occurences
# Compute heuristic 2: don't look at context, just at files touched.
# Collect git blame results for authors in those ranges.
for reviewer, nr_occurences in \
blameOutputCache.get_parsed_git_blame_for(
git_repo, base_revision, path).items():
for reviewer, nr_occurences in blameOutputCache.get_parsed_git_blame_for(
git_repo, base_revision, path
).items():
if reviewer not in reviewers2nr_files_touched:
reviewers2nr_files_touched[reviewer] = 0
reviewers2nr_files_touched[reviewer] += 1
@ -563,30 +644,35 @@ def find_reviewers_for_diff_heuristic(diff):
# Compute "match scores"
total_nr_lines = sum(reviewers2nr_lines_touched.values())
total_nr_files = len(diff.changes)
reviewers_matchscores = \
[(reviewer,
(reviewers2nr_lines_touched.get(reviewer, 0)*100.0/total_nr_lines
if total_nr_lines != 0 else 0,
reviewers2nr_files_touched[reviewer]*100.0/total_nr_files
if total_nr_files != 0 else 0))
for reviewer, nr_lines
in reviewers2nr_files_touched.items()]
reviewers_matchscores = [
(
reviewer,
(
reviewers2nr_lines_touched.get(reviewer, 0) * 100.0 / total_nr_lines
if total_nr_lines != 0
else 0,
reviewers2nr_files_touched[reviewer] * 100.0 / total_nr_files
if total_nr_files != 0
else 0,
),
)
for reviewer, nr_lines in reviewers2nr_files_touched.items()
]
reviewers_matchscores.sort(key=lambda i: i[1], reverse=True)
return reviewers_matchscores
def find_reviewers_for_review(review):
# Process the newest diff first.
diffs = sorted(
review.phabDiffs, key=lambda d: d.dateModified, reverse=True)
diffs = sorted(review.phabDiffs, key=lambda d: d.dateModified, reverse=True)
if len(diffs) == 0:
return
diff = diffs[0]
matched_reviewers = find_reviewers_for_diff_heuristic(diff)
# Show progress, as this is a slow operation:
sys.stdout.write('.')
sys.stdout.write(".")
sys.stdout.flush()
logging.debug(u"matched_reviewers: {0}".format(matched_reviewers))
logging.debug("matched_reviewers: {0}".format(matched_reviewers))
return matched_reviewers
@ -606,58 +692,66 @@ def send_emails(email_addresses, sender, msg):
s.connect()
for email_address in email_addresses:
email_msg = email.mime.multipart.MIMEMultipart()
email_msg['From'] = sender
email_msg['To'] = email_address
email_msg['Subject'] = 'LLVM patches you may be able to review.'
email_msg.attach(email.mime.text.MIMEText(msg.encode('utf-8'), 'plain'))
email_msg["From"] = sender
email_msg["To"] = email_address
email_msg["Subject"] = "LLVM patches you may be able to review."
email_msg.attach(email.mime.text.MIMEText(msg.encode("utf-8"), "plain"))
# python 3.x: s.send_message(email_msg)
s.sendmail(email_msg['From'], email_msg['To'], email_msg.as_string())
s.sendmail(email_msg["From"], email_msg["To"], email_msg.as_string())
s.quit()
def filter_reviewers_to_report_for(people_to_look_for):
# The below is just an example filter, to only report potential reviews
# to do for the people that will receive the report email.
return lambda potential_reviewers: [r for r in potential_reviewers
if r[0] in people_to_look_for]
return lambda potential_reviewers: [
r for r in potential_reviewers if r[0] in people_to_look_for
]
def main():
parser = argparse.ArgumentParser(
description='Match open reviews to potential reviewers.')
description="Match open reviews to potential reviewers."
)
parser.add_argument(
'--no-update-cache',
dest='update_cache',
action='store_false',
"--no-update-cache",
dest="update_cache",
action="store_false",
default=True,
help='Do not update cached Phabricator objects')
help="Do not update cached Phabricator objects",
)
parser.add_argument(
'--email-report',
dest='email_report',
nargs='*',
"--email-report",
dest="email_report",
nargs="*",
default="",
help="A email addresses to send the report to.")
help="A email addresses to send the report to.",
)
parser.add_argument(
'--sender',
dest='sender',
"--sender",
dest="sender",
default="",
help="The email address to use in 'From' on messages emailed out.")
help="The email address to use in 'From' on messages emailed out.",
)
parser.add_argument(
'--email-addresses',
dest='email_addresses',
nargs='*',
help="The email addresses (as known by LLVM git) of " +
"the people to look for reviews for.")
parser.add_argument('--verbose', '-v', action='count')
"--email-addresses",
dest="email_addresses",
nargs="*",
help="The email addresses (as known by LLVM git) of "
+ "the people to look for reviews for.",
)
parser.add_argument("--verbose", "-v", action="count")
args = parser.parse_args()
if args.verbose >= 1:
logging.basicConfig(level=logging.DEBUG)
people_to_look_for = [e.decode('utf-8') for e in args.email_addresses]
logging.debug("Will look for reviews that following contributors could " +
"review: {}".format(people_to_look_for))
people_to_look_for = [e.decode("utf-8") for e in args.email_addresses]
logging.debug(
"Will look for reviews that following contributors could "
+ "review: {}".format(people_to_look_for)
)
logging.debug("Will email a report to: {}".format(args.email_report))
phab = init_phab_connection()
@ -670,7 +764,8 @@ def main():
msg = print_most_recent_reviews(
phab,
days=1,
filter_reviewers=filter_reviewers_to_report_for(people_to_look_for))
filter_reviewers=filter_reviewers_to_report_for(people_to_look_for),
)
if args.email_report != []:
send_emails(args.email_report, args.sender, msg)

View File

@ -2,6 +2,7 @@
from __future__ import print_function
def analyze_match_table(path):
# Extract the instruction table.
data = open(path).read()
@ -14,15 +15,14 @@ def analyze_match_table(path):
for ln in lines:
ln = ln.split("{", 1)[1]
ln = ln.rsplit("}", 1)[0]
a,bc = ln.split("{", 1)
b,c = bc.split("}", 1)
code, string, converter, _ = [s.strip()
for s in a.split(",")]
a, bc = ln.split("{", 1)
b, c = bc.split("}", 1)
code, string, converter, _ = [s.strip() for s in a.split(",")]
items = [s.strip() for s in b.split(",")]
_,features = [s.strip() for s in c.split(",")]
_, features = [s.strip() for s in c.split(",")]
assert string[0] == string[-1] == '"'
string = string[1:-1]
insns.append((code,string,converter,items,features))
insns.append((code, string, converter, items, features))
# For every mnemonic, compute whether or not it can have a carry setting
# operand and whether or not it can have a predication code.
@ -34,24 +34,24 @@ def analyze_match_table(path):
flags.update(items)
mnemonics = set(mnemonic_flags)
ccout_mnemonics = set(m for m in mnemonics
if 'MCK_CCOut' in mnemonic_flags[m])
condcode_mnemonics = set(m for m in mnemonics
if 'MCK_CondCode' in mnemonic_flags[m])
ccout_mnemonics = set(m for m in mnemonics if "MCK_CCOut" in mnemonic_flags[m])
condcode_mnemonics = set(
m for m in mnemonics if "MCK_CondCode" in mnemonic_flags[m]
)
noncondcode_mnemonics = mnemonics - condcode_mnemonics
print(' || '.join('Mnemonic == "%s"' % m
for m in ccout_mnemonics))
print(' || '.join('Mnemonic == "%s"' % m
for m in noncondcode_mnemonics))
print(" || ".join('Mnemonic == "%s"' % m for m in ccout_mnemonics))
print(" || ".join('Mnemonic == "%s"' % m for m in noncondcode_mnemonics))
def main():
import sys
if len(sys.argv) == 1:
import os
from lit.Util import capture
llvm_obj_root = capture(["llvm-config", "--obj-root"])
file = os.path.join(llvm_obj_root,
"lib/Target/ARM/ARMGenAsmMatcher.inc")
file = os.path.join(llvm_obj_root, "lib/Target/ARM/ARMGenAsmMatcher.inc")
elif len(sys.argv) == 2:
file = sys.argv[1]
else:
@ -59,5 +59,6 @@ def main():
analyze_match_table(file)
if __name__ == '__main__':
if __name__ == "__main__":
main()

View File

@ -5,10 +5,12 @@ import sys
from . import common
if sys.version_info[0] > 2:
class string:
expandtabs = str.expandtabs
class string:
expandtabs = str.expandtabs
else:
import string
import string
# RegEx: this is where the magic happens.
@ -16,525 +18,580 @@ else:
ASM_FUNCTION_X86_RE = re.compile(
r'^_?(?P<func>[^:]+):[ \t]*#+[ \t]*(@"?(?P=func)"?| -- Begin function (?P=func))\n(?:\s*\.?Lfunc_begin[^:\n]*:\n)?'
r'(?:\.L(?P=func)\$local:\n)?' # drop .L<func>$local:
r'(?:\s*\.type\s+\.L(?P=func)\$local,@function\n)?' # drop .type .L<func>$local
r'(?:[ \t]*(?:\.cfi_startproc|\.cfi_personality|\.cfi_lsda|\.seh_proc|\.seh_handler)\b[^\n]*\n)*' # drop optional cfi
r'(?P<body>^##?[ \t]+[^:]+:.*?)\s*'
r'^\s*(?:[^:\n]+?:\s*\n\s*\.size|\.cfi_endproc|\.globl|\.comm|\.(?:sub)?section|#+ -- End function)',
flags=(re.M | re.S))
r"(?:\.L(?P=func)\$local:\n)?" # drop .L<func>$local:
r"(?:\s*\.type\s+\.L(?P=func)\$local,@function\n)?" # drop .type .L<func>$local
r"(?:[ \t]*(?:\.cfi_startproc|\.cfi_personality|\.cfi_lsda|\.seh_proc|\.seh_handler)\b[^\n]*\n)*" # drop optional cfi
r"(?P<body>^##?[ \t]+[^:]+:.*?)\s*"
r"^\s*(?:[^:\n]+?:\s*\n\s*\.size|\.cfi_endproc|\.globl|\.comm|\.(?:sub)?section|#+ -- End function)",
flags=(re.M | re.S),
)
ASM_FUNCTION_ARM_RE = re.compile(
r'^(?P<func>[0-9a-zA-Z_$]+):\n' # f: (name of function)
r'(?:\.L(?P=func)\$local:\n)?' # drop .L<func>$local:
r'(?:\s*\.type\s+\.L(?P=func)\$local,@function\n)?' # drop .type .L<func>$local
r'\s+\.fnstart\n' # .fnstart
r'(?P<body>.*?)' # (body of the function)
r'^.Lfunc_end[0-9]+:', # .Lfunc_end0: or # -- End function
flags=(re.M | re.S))
r"^(?P<func>[0-9a-zA-Z_$]+):\n" # f: (name of function)
r"(?:\.L(?P=func)\$local:\n)?" # drop .L<func>$local:
r"(?:\s*\.type\s+\.L(?P=func)\$local,@function\n)?" # drop .type .L<func>$local
r"\s+\.fnstart\n" # .fnstart
r"(?P<body>.*?)" # (body of the function)
r"^.Lfunc_end[0-9]+:", # .Lfunc_end0: or # -- End function
flags=(re.M | re.S),
)
ASM_FUNCTION_AARCH64_RE = re.compile(
r'^_?(?P<func>[^:]+):[ \t]*\/\/[ \t]*@"?(?P=func)"?( (Function|Tail Call))?\n'
r'(?:[ \t]+.cfi_startproc\n)?' # drop optional cfi noise
r'(?P<body>.*?)\n'
# This list is incomplete
r'^\s*(\.Lfunc_end[0-9]+|// -- End function)',
flags=(re.M | re.S))
r'^_?(?P<func>[^:]+):[ \t]*\/\/[ \t]*@"?(?P=func)"?( (Function|Tail Call))?\n'
r"(?:[ \t]+.cfi_startproc\n)?" # drop optional cfi noise
r"(?P<body>.*?)\n"
# This list is incomplete
r"^\s*(\.Lfunc_end[0-9]+|// -- End function)",
flags=(re.M | re.S),
)
ASM_FUNCTION_AMDGPU_RE = re.compile(
r'^_?(?P<func>[^:]+):[ \t]*;+[ \t]*@"?(?P=func)"?\n[^:]*?'
r'(?P<body>.*?)\n' # (body of the function)
r"(?P<body>.*?)\n" # (body of the function)
# This list is incomplete
r'^\s*(\.Lfunc_end[0-9]+:\n|\.section)',
flags=(re.M | re.S))
r"^\s*(\.Lfunc_end[0-9]+:\n|\.section)",
flags=(re.M | re.S),
)
ASM_FUNCTION_BPF_RE = re.compile(
r'^_?(?P<func>[^:]+):[ \t]*#+[ \t]*@"?(?P=func)"?\n'
r'(?:[ \t]+.cfi_startproc\n|.seh_proc[^\n]+\n)?' # drop optional cfi
r'(?P<body>.*?)\s*'
r'.Lfunc_end[0-9]+:\n',
flags=(re.M | re.S))
r"(?:[ \t]+.cfi_startproc\n|.seh_proc[^\n]+\n)?" # drop optional cfi
r"(?P<body>.*?)\s*"
r".Lfunc_end[0-9]+:\n",
flags=(re.M | re.S),
)
ASM_FUNCTION_HEXAGON_RE = re.compile(
r'^_?(?P<func>[^:]+):[ \t]*//[ \t]*@"?(?P=func)"?\n[^:]*?'
r'(?P<body>.*?)\n' # (body of the function)
r"(?P<body>.*?)\n" # (body of the function)
# This list is incomplete
r'.Lfunc_end[0-9]+:\n',
flags=(re.M | re.S))
r".Lfunc_end[0-9]+:\n",
flags=(re.M | re.S),
)
ASM_FUNCTION_M68K_RE = re.compile(
r'^_?(?P<func>[^:]+):[ \t]*;[ \t]*@"?(?P=func)"?\n'
r'(?P<body>.*?)\s*' # (body of the function)
r'.Lfunc_end[0-9]+:\n',
flags=(re.M | re.S))
r"(?P<body>.*?)\s*" # (body of the function)
r".Lfunc_end[0-9]+:\n",
flags=(re.M | re.S),
)
ASM_FUNCTION_MIPS_RE = re.compile(
r'^_?(?P<func>[^:]+):[ \t]*#+[ \t]*@"?(?P=func)"?\n[^:]*?' # f: (name of func)
r'(?:\s*\.?Ltmp[^:\n]*:\n)?[^:]*?' # optional .Ltmp<N> for EH
r'(?:^[ \t]+\.(frame|f?mask|set).*?\n)+' # Mips+LLVM standard asm prologue
r'(?P<body>.*?)\n' # (body of the function)
r'^_?(?P<func>[^:]+):[ \t]*#+[ \t]*@"?(?P=func)"?\n[^:]*?' # f: (name of func)
r"(?:\s*\.?Ltmp[^:\n]*:\n)?[^:]*?" # optional .Ltmp<N> for EH
r"(?:^[ \t]+\.(frame|f?mask|set).*?\n)+" # Mips+LLVM standard asm prologue
r"(?P<body>.*?)\n" # (body of the function)
# Mips+LLVM standard asm epilogue
r'(?:(^[ \t]+\.set[^\n]*?\n)*^[ \t]+\.end.*?\n)'
r'(\$|\.L)func_end[0-9]+:\n', # $func_end0: (mips32 - O32) or
# .Lfunc_end0: (mips64 - NewABI)
flags=(re.M | re.S))
r"(?:(^[ \t]+\.set[^\n]*?\n)*^[ \t]+\.end.*?\n)"
r"(\$|\.L)func_end[0-9]+:\n", # $func_end0: (mips32 - O32) or
# .Lfunc_end0: (mips64 - NewABI)
flags=(re.M | re.S),
)
ASM_FUNCTION_MSP430_RE = re.compile(
r'^_?(?P<func>[^:]+):[ \t]*;+[ \t]*@"?(?P=func)"?\n[^:]*?'
r'(?P<body>.*?)\n'
r'(\$|\.L)func_end[0-9]+:\n', # $func_end0:
flags=(re.M | re.S))
r"(?P<body>.*?)\n"
r"(\$|\.L)func_end[0-9]+:\n", # $func_end0:
flags=(re.M | re.S),
)
ASM_FUNCTION_AVR_RE = re.compile(
r'^_?(?P<func>[^:]+):[ \t]*;+[ \t]*@"?(?P=func)"?\n[^:]*?'
r'(?P<body>.*?)\n'
r'.Lfunc_end[0-9]+:\n',
flags=(re.M | re.S))
r"(?P<body>.*?)\n"
r".Lfunc_end[0-9]+:\n",
flags=(re.M | re.S),
)
ASM_FUNCTION_PPC_RE = re.compile(
r'#[ \-\t]*Begin function (?P<func>[^.:]+)\n'
r'.*?'
r"#[ \-\t]*Begin function (?P<func>[^.:]+)\n"
r".*?"
r'^[_.]?(?P=func):(?:[ \t]*#+[ \t]*@"?(?P=func)"?)?\n'
r'(?:^[^#]*\n)*'
r'(?P<body>.*?)\n'
r"(?:^[^#]*\n)*"
r"(?P<body>.*?)\n"
# This list is incomplete
r'(?:^[ \t]*(?:\.(?:long|quad|v?byte)[ \t]+[^\n]+)\n)*'
r'(?:\.Lfunc_end|L\.\.(?P=func))[0-9]+:\n',
flags=(re.M | re.S))
r"(?:^[ \t]*(?:\.(?:long|quad|v?byte)[ \t]+[^\n]+)\n)*"
r"(?:\.Lfunc_end|L\.\.(?P=func))[0-9]+:\n",
flags=(re.M | re.S),
)
ASM_FUNCTION_RISCV_RE = re.compile(
r'^_?(?P<func>[^:]+):[ \t]*#+[ \t]*@"?(?P=func)"?\n'
r'(?:\s*\.?L(?P=func)\$local:\n)?' # optional .L<func>$local: due to -fno-semantic-interposition
r'(?:\s*\.type\s+\.?L(?P=func)\$local,@function\n)?' # optional .type .L<func>$local
r'(?:\s*\.?Lfunc_begin[^:\n]*:\n)?[^:]*?'
r'(?P<body>^##?[ \t]+[^:]+:.*?)\s*'
r'.Lfunc_end[0-9]+:\n',
flags=(re.M | re.S))
r"(?:\s*\.?L(?P=func)\$local:\n)?" # optional .L<func>$local: due to -fno-semantic-interposition
r"(?:\s*\.type\s+\.?L(?P=func)\$local,@function\n)?" # optional .type .L<func>$local
r"(?:\s*\.?Lfunc_begin[^:\n]*:\n)?[^:]*?"
r"(?P<body>^##?[ \t]+[^:]+:.*?)\s*"
r".Lfunc_end[0-9]+:\n",
flags=(re.M | re.S),
)
ASM_FUNCTION_LANAI_RE = re.compile(
r'^_?(?P<func>[^:]+):[ \t]*!+[ \t]*@"?(?P=func)"?\n'
r'(?:[ \t]+.cfi_startproc\n)?' # drop optional cfi noise
r'(?P<body>.*?)\s*'
r'.Lfunc_end[0-9]+:\n',
flags=(re.M | re.S))
r"(?:[ \t]+.cfi_startproc\n)?" # drop optional cfi noise
r"(?P<body>.*?)\s*"
r".Lfunc_end[0-9]+:\n",
flags=(re.M | re.S),
)
ASM_FUNCTION_SPARC_RE = re.compile(
r'^_?(?P<func>[^:]+):[ \t]*!+[ \t]*@"?(?P=func)"?\n'
r'(?P<body>.*?)\s*'
r'.Lfunc_end[0-9]+:\n',
flags=(re.M | re.S))
r"(?P<body>.*?)\s*"
r".Lfunc_end[0-9]+:\n",
flags=(re.M | re.S),
)
ASM_FUNCTION_SYSTEMZ_RE = re.compile(
r'^_?(?P<func>[^:]+):[ \t]*#+[ \t]*@"?(?P=func)"?\n'
r'(?:[ \t]+.cfi_startproc\n)?'
r'(?P<body>.*?)\n'
r'.Lfunc_end[0-9]+:\n',
flags=(re.M | re.S))
r"(?:[ \t]+.cfi_startproc\n)?"
r"(?P<body>.*?)\n"
r".Lfunc_end[0-9]+:\n",
flags=(re.M | re.S),
)
ASM_FUNCTION_AARCH64_DARWIN_RE = re.compile(
r'^_(?P<func>[^:]+):[ \t]*;[ \t]@"?(?P=func)"?\n'
r'([ \t]*.cfi_startproc\n[\s]*)?'
r'(?P<body>.*?)'
r'([ \t]*.cfi_endproc\n[\s]*)?'
r'^[ \t]*;[ \t]--[ \t]End[ \t]function',
flags=(re.M | re.S))
r"([ \t]*.cfi_startproc\n[\s]*)?"
r"(?P<body>.*?)"
r"([ \t]*.cfi_endproc\n[\s]*)?"
r"^[ \t]*;[ \t]--[ \t]End[ \t]function",
flags=(re.M | re.S),
)
ASM_FUNCTION_ARM_DARWIN_RE = re.compile(
r'@[ \t]--[ \t]Begin[ \t]function[ \t](?P<func>[^ \t]+?)\n'
r'^[ \t]*\.globl[ \t]*_(?P=func)[ \t]*'
r'(?P<directives>.*?)'
r'^_(?P=func):\n[ \t]*'
r'(?P<body>.*?)'
r'^[ \t]*@[ \t]--[ \t]End[ \t]function',
flags=(re.M | re.S ))
r"@[ \t]--[ \t]Begin[ \t]function[ \t](?P<func>[^ \t]+?)\n"
r"^[ \t]*\.globl[ \t]*_(?P=func)[ \t]*"
r"(?P<directives>.*?)"
r"^_(?P=func):\n[ \t]*"
r"(?P<body>.*?)"
r"^[ \t]*@[ \t]--[ \t]End[ \t]function",
flags=(re.M | re.S),
)
ASM_FUNCTION_ARM_MACHO_RE = re.compile(
r'^_(?P<func>[^:]+):[ \t]*\n'
r'([ \t]*.cfi_startproc\n[ \t]*)?'
r'(?P<body>.*?)\n'
r'[ \t]*\.cfi_endproc\n',
flags=(re.M | re.S))
r"^_(?P<func>[^:]+):[ \t]*\n"
r"([ \t]*.cfi_startproc\n[ \t]*)?"
r"(?P<body>.*?)\n"
r"[ \t]*\.cfi_endproc\n",
flags=(re.M | re.S),
)
ASM_FUNCTION_THUMBS_DARWIN_RE = re.compile(
r'^_(?P<func>[^:]+):\n'
r'(?P<body>.*?)\n'
r'[ \t]*\.data_region\n',
flags=(re.M | re.S))
r"^_(?P<func>[^:]+):\n" r"(?P<body>.*?)\n" r"[ \t]*\.data_region\n",
flags=(re.M | re.S),
)
ASM_FUNCTION_THUMB_DARWIN_RE = re.compile(
r'^_(?P<func>[^:]+):\n'
r'(?P<body>.*?)\n'
r'^[ \t]*@[ \t]--[ \t]End[ \t]function',
flags=(re.M | re.S))
r"^_(?P<func>[^:]+):\n" r"(?P<body>.*?)\n" r"^[ \t]*@[ \t]--[ \t]End[ \t]function",
flags=(re.M | re.S),
)
ASM_FUNCTION_ARM_IOS_RE = re.compile(
r'^_(?P<func>[^:]+):\n'
r'(?P<body>.*?)'
r'^[ \t]*@[ \t]--[ \t]End[ \t]function',
flags=(re.M | re.S))
r"^_(?P<func>[^:]+):\n" r"(?P<body>.*?)" r"^[ \t]*@[ \t]--[ \t]End[ \t]function",
flags=(re.M | re.S),
)
ASM_FUNCTION_WASM_RE = re.compile(
r'^_?(?P<func>[^:]+):[ \t]*#+[ \t]*@"?(?P=func)"?\n'
r'(?P<body>.*?)\n'
r'^\s*(\.Lfunc_end[0-9]+:\n|end_function)',
flags=(re.M | re.S))
r"(?P<body>.*?)\n"
r"^\s*(\.Lfunc_end[0-9]+:\n|end_function)",
flags=(re.M | re.S),
)
ASM_FUNCTION_VE_RE = re.compile(
r'^_?(?P<func>[^:]+):[ \t]*#+[ \t]*@(?P=func)\n'
r'(?:\s*\.?L(?P=func)\$local:\n)?' # optional .L<func>$local: due to -fno-semantic-interposition
r'(?:\s*\.type\s+\.?L(?P=func)\$local,@function\n)?' # optional .type .L<func>$local
r'(?:\s*\.?Lfunc_begin[^:\n]*:\n)?[^:]*?'
r'(?P<body>^##?[ \t]+[^:]+:.*?)\s*'
r'.Lfunc_end[0-9]+:\n',
flags=(re.M | re.S))
r"^_?(?P<func>[^:]+):[ \t]*#+[ \t]*@(?P=func)\n"
r"(?:\s*\.?L(?P=func)\$local:\n)?" # optional .L<func>$local: due to -fno-semantic-interposition
r"(?:\s*\.type\s+\.?L(?P=func)\$local,@function\n)?" # optional .type .L<func>$local
r"(?:\s*\.?Lfunc_begin[^:\n]*:\n)?[^:]*?"
r"(?P<body>^##?[ \t]+[^:]+:.*?)\s*"
r".Lfunc_end[0-9]+:\n",
flags=(re.M | re.S),
)
ASM_FUNCTION_CSKY_RE = re.compile(
r'^_?(?P<func>[^:]+):[ \t]*#+[ \t]*@(?P=func)\n(?:\s*\.?Lfunc_begin[^:\n]*:\n)?[^:]*?'
r'(?P<body>^##?[ \t]+[^:]+:.*?)\s*'
r'.Lfunc_end[0-9]+:\n',
flags=(re.M | re.S))
r"^_?(?P<func>[^:]+):[ \t]*#+[ \t]*@(?P=func)\n(?:\s*\.?Lfunc_begin[^:\n]*:\n)?[^:]*?"
r"(?P<body>^##?[ \t]+[^:]+:.*?)\s*"
r".Lfunc_end[0-9]+:\n",
flags=(re.M | re.S),
)
ASM_FUNCTION_NVPTX_RE = re.compile(
# function attributes and retval
# .visible .func (.param .align 16 .b8 func_retval0[32])
#r'^(\.visible\s+)?\.func\s+(\([^\)]*\)\s*)?'
r'^(\.(func|visible|weak|entry|noreturn|extern)\s+)+(\([^\)]*\)\s*)?'
# r'^(\.visible\s+)?\.func\s+(\([^\)]*\)\s*)?'
r"^(\.(func|visible|weak|entry|noreturn|extern)\s+)+(\([^\)]*\)\s*)?"
# function name
r'(?P<func>[^\(\n]+)'
r"(?P<func>[^\(\n]+)"
# function name separator (opening brace)
r'(?P<func_name_separator>\()'
r"(?P<func_name_separator>\()"
# function parameters
# (
# .param .align 16 .b8 callee_St8x4_param_0[32]
# ) // -- Begin function callee_St8x4
r'[^\)]*\)(\s*//[^\n]*)?\n'
r"[^\)]*\)(\s*//[^\n]*)?\n"
# function body
r'(?P<body>.*?)\n'
r"(?P<body>.*?)\n"
# function body end marker
r'\s*// -- End function',
flags=(re.M | re.S))
r"\s*// -- End function",
flags=(re.M | re.S),
)
ASM_FUNCTION_LOONGARCH_RE = re.compile(
r'^_?(?P<func>[^:]+):[ \t]*#+[ \t]*@"?(?P=func)"?\n'
r'(?:\s*\.?Lfunc_begin[^:\n]*:\n)?[^:]*?'
r'(?P<body>^##?[ \t]+[^:]+:.*?)\s*'
r'.Lfunc_end[0-9]+:\n',
flags=(re.M | re.S))
r"(?:\s*\.?Lfunc_begin[^:\n]*:\n)?[^:]*?"
r"(?P<body>^##?[ \t]+[^:]+:.*?)\s*"
r".Lfunc_end[0-9]+:\n",
flags=(re.M | re.S),
)
SCRUB_X86_SHUFFLES_RE = (
re.compile(
r'^(\s*\w+) [^#\n]+#+ ((?:[xyz]mm\d+|mem)( \{%k\d+\}( \{z\})?)? = .*)$',
flags=re.M))
SCRUB_X86_SHUFFLES_RE = re.compile(
r"^(\s*\w+) [^#\n]+#+ ((?:[xyz]mm\d+|mem)( \{%k\d+\}( \{z\})?)? = .*)$", flags=re.M
)
SCRUB_X86_SHUFFLES_NO_MEM_RE = (
re.compile(
r'^(\s*\w+) [^#\n]+#+ ((?:[xyz]mm\d+|mem)( \{%k\d+\}( \{z\})?)? = (?!.*(?:mem)).*)$',
flags=re.M))
SCRUB_X86_SHUFFLES_NO_MEM_RE = re.compile(
r"^(\s*\w+) [^#\n]+#+ ((?:[xyz]mm\d+|mem)( \{%k\d+\}( \{z\})?)? = (?!.*(?:mem)).*)$",
flags=re.M,
)
SCRUB_X86_SPILL_RELOAD_RE = re.compile(
r"-?\d+\(%([er])[sb]p\)(.*(?:Spill|Reload))$", flags=re.M
)
SCRUB_X86_SP_RE = re.compile(r"\d+\(%(esp|rsp)\)")
SCRUB_X86_RIP_RE = re.compile(r"[.\w]+\(%rip\)")
SCRUB_X86_LCP_RE = re.compile(r"\.?LCPI[0-9]+_[0-9]+")
SCRUB_X86_RET_RE = re.compile(r"ret[l|q]")
SCRUB_X86_SPILL_RELOAD_RE = (
re.compile(
r'-?\d+\(%([er])[sb]p\)(.*(?:Spill|Reload))$',
flags=re.M))
SCRUB_X86_SP_RE = re.compile(r'\d+\(%(esp|rsp)\)')
SCRUB_X86_RIP_RE = re.compile(r'[.\w]+\(%rip\)')
SCRUB_X86_LCP_RE = re.compile(r'\.?LCPI[0-9]+_[0-9]+')
SCRUB_X86_RET_RE = re.compile(r'ret[l|q]')
def scrub_asm_x86(asm, args):
# Scrub runs of whitespace out of the assembly, but leave the leading
# whitespace in place.
asm = common.SCRUB_WHITESPACE_RE.sub(r' ', asm)
# Expand the tabs used for indentation.
asm = string.expandtabs(asm, 2)
# Scrub runs of whitespace out of the assembly, but leave the leading
# whitespace in place.
asm = common.SCRUB_WHITESPACE_RE.sub(r" ", asm)
# Expand the tabs used for indentation.
asm = string.expandtabs(asm, 2)
# Detect shuffle asm comments and hide the operands in favor of the comments.
if getattr(args, 'no_x86_scrub_mem_shuffle', True):
asm = SCRUB_X86_SHUFFLES_NO_MEM_RE.sub(r'\1 {{.*#+}} \2', asm)
else:
asm = SCRUB_X86_SHUFFLES_RE.sub(r'\1 {{.*#+}} \2', asm)
# Detect shuffle asm comments and hide the operands in favor of the comments.
if getattr(args, "no_x86_scrub_mem_shuffle", True):
asm = SCRUB_X86_SHUFFLES_NO_MEM_RE.sub(r"\1 {{.*#+}} \2", asm)
else:
asm = SCRUB_X86_SHUFFLES_RE.sub(r"\1 {{.*#+}} \2", asm)
# Detect stack spills and reloads and hide their exact offset and whether
# they used the stack pointer or frame pointer.
asm = SCRUB_X86_SPILL_RELOAD_RE.sub(r"{{[-0-9]+}}(%\1{{[sb]}}p)\2", asm)
if getattr(args, "x86_scrub_sp", True):
# Generically match the stack offset of a memory operand.
asm = SCRUB_X86_SP_RE.sub(r"{{[0-9]+}}(%\1)", asm)
if getattr(args, "x86_scrub_rip", False):
# Generically match a RIP-relative memory operand.
asm = SCRUB_X86_RIP_RE.sub(r"{{.*}}(%rip)", asm)
# Generically match a LCP symbol.
asm = SCRUB_X86_LCP_RE.sub(r"{{\.?LCPI[0-9]+_[0-9]+}}", asm)
if getattr(args, "extra_scrub", False):
# Avoid generating different checks for 32- and 64-bit because of 'retl' vs 'retq'.
asm = SCRUB_X86_RET_RE.sub(r"ret{{[l|q]}}", asm)
# Strip kill operands inserted into the asm.
asm = common.SCRUB_KILL_COMMENT_RE.sub("", asm)
# Strip trailing whitespace.
asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r"", asm)
return asm
# Detect stack spills and reloads and hide their exact offset and whether
# they used the stack pointer or frame pointer.
asm = SCRUB_X86_SPILL_RELOAD_RE.sub(r'{{[-0-9]+}}(%\1{{[sb]}}p)\2', asm)
if getattr(args, 'x86_scrub_sp', True):
# Generically match the stack offset of a memory operand.
asm = SCRUB_X86_SP_RE.sub(r'{{[0-9]+}}(%\1)', asm)
if getattr(args, 'x86_scrub_rip', False):
# Generically match a RIP-relative memory operand.
asm = SCRUB_X86_RIP_RE.sub(r'{{.*}}(%rip)', asm)
# Generically match a LCP symbol.
asm = SCRUB_X86_LCP_RE.sub(r'{{\.?LCPI[0-9]+_[0-9]+}}', asm)
if getattr(args, 'extra_scrub', False):
# Avoid generating different checks for 32- and 64-bit because of 'retl' vs 'retq'.
asm = SCRUB_X86_RET_RE.sub(r'ret{{[l|q]}}', asm)
# Strip kill operands inserted into the asm.
asm = common.SCRUB_KILL_COMMENT_RE.sub('', asm)
# Strip trailing whitespace.
asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r'', asm)
return asm
def scrub_asm_amdgpu(asm, args):
# Scrub runs of whitespace out of the assembly, but leave the leading
# whitespace in place.
asm = common.SCRUB_WHITESPACE_RE.sub(r' ', asm)
# Expand the tabs used for indentation.
asm = string.expandtabs(asm, 2)
# Strip trailing whitespace.
asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r'', asm)
return asm
# Scrub runs of whitespace out of the assembly, but leave the leading
# whitespace in place.
asm = common.SCRUB_WHITESPACE_RE.sub(r" ", asm)
# Expand the tabs used for indentation.
asm = string.expandtabs(asm, 2)
# Strip trailing whitespace.
asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r"", asm)
return asm
def scrub_asm_arm_eabi(asm, args):
# Scrub runs of whitespace out of the assembly, but leave the leading
# whitespace in place.
asm = common.SCRUB_WHITESPACE_RE.sub(r' ', asm)
# Expand the tabs used for indentation.
asm = string.expandtabs(asm, 2)
# Strip kill operands inserted into the asm.
asm = common.SCRUB_KILL_COMMENT_RE.sub('', asm)
# Strip trailing whitespace.
asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r'', asm)
return asm
# Scrub runs of whitespace out of the assembly, but leave the leading
# whitespace in place.
asm = common.SCRUB_WHITESPACE_RE.sub(r" ", asm)
# Expand the tabs used for indentation.
asm = string.expandtabs(asm, 2)
# Strip kill operands inserted into the asm.
asm = common.SCRUB_KILL_COMMENT_RE.sub("", asm)
# Strip trailing whitespace.
asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r"", asm)
return asm
def scrub_asm_bpf(asm, args):
# Scrub runs of whitespace out of the assembly, but leave the leading
# whitespace in place.
asm = common.SCRUB_WHITESPACE_RE.sub(r' ', asm)
# Expand the tabs used for indentation.
asm = string.expandtabs(asm, 2)
# Strip trailing whitespace.
asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r'', asm)
return asm
# Scrub runs of whitespace out of the assembly, but leave the leading
# whitespace in place.
asm = common.SCRUB_WHITESPACE_RE.sub(r" ", asm)
# Expand the tabs used for indentation.
asm = string.expandtabs(asm, 2)
# Strip trailing whitespace.
asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r"", asm)
return asm
def scrub_asm_hexagon(asm, args):
# Scrub runs of whitespace out of the assembly, but leave the leading
# whitespace in place.
asm = common.SCRUB_WHITESPACE_RE.sub(r' ', asm)
# Expand the tabs used for indentation.
asm = string.expandtabs(asm, 2)
# Strip trailing whitespace.
asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r'', asm)
return asm
# Scrub runs of whitespace out of the assembly, but leave the leading
# whitespace in place.
asm = common.SCRUB_WHITESPACE_RE.sub(r" ", asm)
# Expand the tabs used for indentation.
asm = string.expandtabs(asm, 2)
# Strip trailing whitespace.
asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r"", asm)
return asm
def scrub_asm_powerpc(asm, args):
# Scrub runs of whitespace out of the assembly, but leave the leading
# whitespace in place.
asm = common.SCRUB_WHITESPACE_RE.sub(r' ', asm)
# Expand the tabs used for indentation.
asm = string.expandtabs(asm, 2)
# Strip unimportant comments, but leave the token '#' in place.
asm = common.SCRUB_LOOP_COMMENT_RE.sub(r'#', asm)
# Strip trailing whitespace.
asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r'', asm)
# Strip the tailing token '#', except the line only has token '#'.
asm = common.SCRUB_TAILING_COMMENT_TOKEN_RE.sub(r'', asm)
return asm
# Scrub runs of whitespace out of the assembly, but leave the leading
# whitespace in place.
asm = common.SCRUB_WHITESPACE_RE.sub(r" ", asm)
# Expand the tabs used for indentation.
asm = string.expandtabs(asm, 2)
# Strip unimportant comments, but leave the token '#' in place.
asm = common.SCRUB_LOOP_COMMENT_RE.sub(r"#", asm)
# Strip trailing whitespace.
asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r"", asm)
# Strip the tailing token '#', except the line only has token '#'.
asm = common.SCRUB_TAILING_COMMENT_TOKEN_RE.sub(r"", asm)
return asm
def scrub_asm_m68k(asm, args):
# Scrub runs of whitespace out of the assembly, but leave the leading
# whitespace in place.
asm = common.SCRUB_WHITESPACE_RE.sub(r' ', asm)
# Expand the tabs used for indentation.
asm = string.expandtabs(asm, 2)
# Strip trailing whitespace.
asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r'', asm)
return asm
# Scrub runs of whitespace out of the assembly, but leave the leading
# whitespace in place.
asm = common.SCRUB_WHITESPACE_RE.sub(r" ", asm)
# Expand the tabs used for indentation.
asm = string.expandtabs(asm, 2)
# Strip trailing whitespace.
asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r"", asm)
return asm
def scrub_asm_mips(asm, args):
# Scrub runs of whitespace out of the assembly, but leave the leading
# whitespace in place.
asm = common.SCRUB_WHITESPACE_RE.sub(r' ', asm)
# Expand the tabs used for indentation.
asm = string.expandtabs(asm, 2)
# Strip trailing whitespace.
asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r'', asm)
return asm
# Scrub runs of whitespace out of the assembly, but leave the leading
# whitespace in place.
asm = common.SCRUB_WHITESPACE_RE.sub(r" ", asm)
# Expand the tabs used for indentation.
asm = string.expandtabs(asm, 2)
# Strip trailing whitespace.
asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r"", asm)
return asm
def scrub_asm_msp430(asm, args):
# Scrub runs of whitespace out of the assembly, but leave the leading
# whitespace in place.
asm = common.SCRUB_WHITESPACE_RE.sub(r' ', asm)
# Expand the tabs used for indentation.
asm = string.expandtabs(asm, 2)
# Strip trailing whitespace.
asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r'', asm)
return asm
# Scrub runs of whitespace out of the assembly, but leave the leading
# whitespace in place.
asm = common.SCRUB_WHITESPACE_RE.sub(r" ", asm)
# Expand the tabs used for indentation.
asm = string.expandtabs(asm, 2)
# Strip trailing whitespace.
asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r"", asm)
return asm
def scrub_asm_avr(asm, args):
# Scrub runs of whitespace out of the assembly, but leave the leading
# whitespace in place.
asm = common.SCRUB_WHITESPACE_RE.sub(r' ', asm)
# Expand the tabs used for indentation.
asm = string.expandtabs(asm, 2)
# Strip trailing whitespace.
asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r'', asm)
return asm
# Scrub runs of whitespace out of the assembly, but leave the leading
# whitespace in place.
asm = common.SCRUB_WHITESPACE_RE.sub(r" ", asm)
# Expand the tabs used for indentation.
asm = string.expandtabs(asm, 2)
# Strip trailing whitespace.
asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r"", asm)
return asm
def scrub_asm_riscv(asm, args):
# Scrub runs of whitespace out of the assembly, but leave the leading
# whitespace in place.
asm = common.SCRUB_WHITESPACE_RE.sub(r' ', asm)
# Expand the tabs used for indentation.
asm = string.expandtabs(asm, 2)
# Strip trailing whitespace.
asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r'', asm)
return asm
# Scrub runs of whitespace out of the assembly, but leave the leading
# whitespace in place.
asm = common.SCRUB_WHITESPACE_RE.sub(r" ", asm)
# Expand the tabs used for indentation.
asm = string.expandtabs(asm, 2)
# Strip trailing whitespace.
asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r"", asm)
return asm
def scrub_asm_lanai(asm, args):
# Scrub runs of whitespace out of the assembly, but leave the leading
# whitespace in place.
asm = common.SCRUB_WHITESPACE_RE.sub(r' ', asm)
# Expand the tabs used for indentation.
asm = string.expandtabs(asm, 2)
# Strip trailing whitespace.
asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r'', asm)
return asm
# Scrub runs of whitespace out of the assembly, but leave the leading
# whitespace in place.
asm = common.SCRUB_WHITESPACE_RE.sub(r" ", asm)
# Expand the tabs used for indentation.
asm = string.expandtabs(asm, 2)
# Strip trailing whitespace.
asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r"", asm)
return asm
def scrub_asm_sparc(asm, args):
# Scrub runs of whitespace out of the assembly, but leave the leading
# whitespace in place.
asm = common.SCRUB_WHITESPACE_RE.sub(r' ', asm)
# Expand the tabs used for indentation.
asm = string.expandtabs(asm, 2)
# Strip trailing whitespace.
asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r'', asm)
return asm
# Scrub runs of whitespace out of the assembly, but leave the leading
# whitespace in place.
asm = common.SCRUB_WHITESPACE_RE.sub(r" ", asm)
# Expand the tabs used for indentation.
asm = string.expandtabs(asm, 2)
# Strip trailing whitespace.
asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r"", asm)
return asm
def scrub_asm_systemz(asm, args):
# Scrub runs of whitespace out of the assembly, but leave the leading
# whitespace in place.
asm = common.SCRUB_WHITESPACE_RE.sub(r' ', asm)
# Expand the tabs used for indentation.
asm = string.expandtabs(asm, 2)
# Strip trailing whitespace.
asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r'', asm)
return asm
# Scrub runs of whitespace out of the assembly, but leave the leading
# whitespace in place.
asm = common.SCRUB_WHITESPACE_RE.sub(r" ", asm)
# Expand the tabs used for indentation.
asm = string.expandtabs(asm, 2)
# Strip trailing whitespace.
asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r"", asm)
return asm
def scrub_asm_wasm(asm, args):
# Scrub runs of whitespace out of the assembly, but leave the leading
# whitespace in place.
asm = common.SCRUB_WHITESPACE_RE.sub(r' ', asm)
# Expand the tabs used for indentation.
asm = string.expandtabs(asm, 2)
# Strip trailing whitespace.
asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r'', asm)
return asm
# Scrub runs of whitespace out of the assembly, but leave the leading
# whitespace in place.
asm = common.SCRUB_WHITESPACE_RE.sub(r" ", asm)
# Expand the tabs used for indentation.
asm = string.expandtabs(asm, 2)
# Strip trailing whitespace.
asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r"", asm)
return asm
def scrub_asm_ve(asm, args):
# Scrub runs of whitespace out of the assembly, but leave the leading
# whitespace in place.
asm = common.SCRUB_WHITESPACE_RE.sub(r' ', asm)
# Expand the tabs used for indentation.
asm = string.expandtabs(asm, 2)
# Strip trailing whitespace.
asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r'', asm)
return asm
# Scrub runs of whitespace out of the assembly, but leave the leading
# whitespace in place.
asm = common.SCRUB_WHITESPACE_RE.sub(r" ", asm)
# Expand the tabs used for indentation.
asm = string.expandtabs(asm, 2)
# Strip trailing whitespace.
asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r"", asm)
return asm
def scrub_asm_csky(asm, args):
# Scrub runs of whitespace out of the assembly, but leave the leading
# whitespace in place.
asm = common.SCRUB_WHITESPACE_RE.sub(r' ', asm)
# Expand the tabs used for indentation.
asm = string.expandtabs(asm, 2)
# Strip kill operands inserted into the asm.
asm = common.SCRUB_KILL_COMMENT_RE.sub('', asm)
# Strip trailing whitespace.
asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r'', asm)
return asm
# Scrub runs of whitespace out of the assembly, but leave the leading
# whitespace in place.
asm = common.SCRUB_WHITESPACE_RE.sub(r" ", asm)
# Expand the tabs used for indentation.
asm = string.expandtabs(asm, 2)
# Strip kill operands inserted into the asm.
asm = common.SCRUB_KILL_COMMENT_RE.sub("", asm)
# Strip trailing whitespace.
asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r"", asm)
return asm
def scrub_asm_nvptx(asm, args):
# Scrub runs of whitespace out of the assembly, but leave the leading
# whitespace in place.
asm = common.SCRUB_WHITESPACE_RE.sub(r' ', asm)
# Expand the tabs used for indentation.
asm = string.expandtabs(asm, 2)
# Strip trailing whitespace.
asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r'', asm)
return asm
# Scrub runs of whitespace out of the assembly, but leave the leading
# whitespace in place.
asm = common.SCRUB_WHITESPACE_RE.sub(r" ", asm)
# Expand the tabs used for indentation.
asm = string.expandtabs(asm, 2)
# Strip trailing whitespace.
asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r"", asm)
return asm
def scrub_asm_loongarch(asm, args):
# Scrub runs of whitespace out of the assembly, but leave the leading
# whitespace in place.
asm = common.SCRUB_WHITESPACE_RE.sub(r' ', asm)
# Expand the tabs used for indentation.
asm = string.expandtabs(asm, 2)
# Strip trailing whitespace.
asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r'', asm)
return asm
# Scrub runs of whitespace out of the assembly, but leave the leading
# whitespace in place.
asm = common.SCRUB_WHITESPACE_RE.sub(r" ", asm)
# Expand the tabs used for indentation.
asm = string.expandtabs(asm, 2)
# Strip trailing whitespace.
asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r"", asm)
return asm
# Returns a tuple of a scrub function and a function regex. Scrub function is
# used to alter function body in some way, for example, remove trailing spaces.
# Function regex is used to match function name, body, etc. in raw llc output.
def get_run_handler(triple):
target_handlers = {
'i686': (scrub_asm_x86, ASM_FUNCTION_X86_RE),
'x86': (scrub_asm_x86, ASM_FUNCTION_X86_RE),
'i386': (scrub_asm_x86, ASM_FUNCTION_X86_RE),
'arm64_32-apple-ios': (scrub_asm_arm_eabi, ASM_FUNCTION_AARCH64_DARWIN_RE),
'arm64_32-apple-watchos2.0.0': (scrub_asm_arm_eabi, ASM_FUNCTION_AARCH64_DARWIN_RE),
'aarch64': (scrub_asm_arm_eabi, ASM_FUNCTION_AARCH64_RE),
'aarch64-apple-darwin': (scrub_asm_arm_eabi, ASM_FUNCTION_AARCH64_DARWIN_RE),
'aarch64-apple-ios': (scrub_asm_arm_eabi, ASM_FUNCTION_AARCH64_DARWIN_RE),
'bpf': (scrub_asm_bpf, ASM_FUNCTION_BPF_RE),
'bpfel': (scrub_asm_bpf, ASM_FUNCTION_BPF_RE),
'bpfeb': (scrub_asm_bpf, ASM_FUNCTION_BPF_RE),
'hexagon': (scrub_asm_hexagon, ASM_FUNCTION_HEXAGON_RE),
'r600': (scrub_asm_amdgpu, ASM_FUNCTION_AMDGPU_RE),
'amdgcn': (scrub_asm_amdgpu, ASM_FUNCTION_AMDGPU_RE),
'arm': (scrub_asm_arm_eabi, ASM_FUNCTION_ARM_RE),
'arm64': (scrub_asm_arm_eabi, ASM_FUNCTION_AARCH64_RE),
'arm64e': (scrub_asm_arm_eabi, ASM_FUNCTION_AARCH64_DARWIN_RE),
'arm64ec': (scrub_asm_arm_eabi, ASM_FUNCTION_AARCH64_RE),
'arm64-apple-ios': (scrub_asm_arm_eabi, ASM_FUNCTION_AARCH64_DARWIN_RE),
'armv7-apple-ios' : (scrub_asm_arm_eabi, ASM_FUNCTION_ARM_IOS_RE),
'armv7-apple-darwin': (scrub_asm_arm_eabi, ASM_FUNCTION_ARM_DARWIN_RE),
'thumb': (scrub_asm_arm_eabi, ASM_FUNCTION_ARM_RE),
'thumb-macho': (scrub_asm_arm_eabi, ASM_FUNCTION_ARM_MACHO_RE),
'thumbv5-macho': (scrub_asm_arm_eabi, ASM_FUNCTION_ARM_MACHO_RE),
'thumbv7s-apple-darwin' : (scrub_asm_arm_eabi, ASM_FUNCTION_THUMBS_DARWIN_RE),
'thumbv7-apple-darwin' : (scrub_asm_arm_eabi, ASM_FUNCTION_THUMB_DARWIN_RE),
'thumbv7-apple-ios' : (scrub_asm_arm_eabi, ASM_FUNCTION_ARM_IOS_RE),
'm68k': (scrub_asm_m68k, ASM_FUNCTION_M68K_RE),
'mips': (scrub_asm_mips, ASM_FUNCTION_MIPS_RE),
'msp430': (scrub_asm_msp430, ASM_FUNCTION_MSP430_RE),
'avr': (scrub_asm_avr, ASM_FUNCTION_AVR_RE),
'ppc32': (scrub_asm_powerpc, ASM_FUNCTION_PPC_RE),
'ppc64': (scrub_asm_powerpc, ASM_FUNCTION_PPC_RE),
'powerpc': (scrub_asm_powerpc, ASM_FUNCTION_PPC_RE),
'riscv32': (scrub_asm_riscv, ASM_FUNCTION_RISCV_RE),
'riscv64': (scrub_asm_riscv, ASM_FUNCTION_RISCV_RE),
'lanai': (scrub_asm_lanai, ASM_FUNCTION_LANAI_RE),
'sparc': (scrub_asm_sparc, ASM_FUNCTION_SPARC_RE),
's390x': (scrub_asm_systemz, ASM_FUNCTION_SYSTEMZ_RE),
'wasm32': (scrub_asm_wasm, ASM_FUNCTION_WASM_RE),
'wasm64': (scrub_asm_wasm, ASM_FUNCTION_WASM_RE),
've': (scrub_asm_ve, ASM_FUNCTION_VE_RE),
'csky': (scrub_asm_csky, ASM_FUNCTION_CSKY_RE),
'nvptx': (scrub_asm_nvptx, ASM_FUNCTION_NVPTX_RE),
'loongarch32': (scrub_asm_loongarch, ASM_FUNCTION_LOONGARCH_RE),
'loongarch64': (scrub_asm_loongarch, ASM_FUNCTION_LOONGARCH_RE)
}
handler = None
best_prefix = ''
for prefix, s in target_handlers.items():
if triple.startswith(prefix) and len(prefix) > len(best_prefix):
handler = s
best_prefix = prefix
target_handlers = {
"i686": (scrub_asm_x86, ASM_FUNCTION_X86_RE),
"x86": (scrub_asm_x86, ASM_FUNCTION_X86_RE),
"i386": (scrub_asm_x86, ASM_FUNCTION_X86_RE),
"arm64_32-apple-ios": (scrub_asm_arm_eabi, ASM_FUNCTION_AARCH64_DARWIN_RE),
"arm64_32-apple-watchos2.0.0": (
scrub_asm_arm_eabi,
ASM_FUNCTION_AARCH64_DARWIN_RE,
),
"aarch64": (scrub_asm_arm_eabi, ASM_FUNCTION_AARCH64_RE),
"aarch64-apple-darwin": (scrub_asm_arm_eabi, ASM_FUNCTION_AARCH64_DARWIN_RE),
"aarch64-apple-ios": (scrub_asm_arm_eabi, ASM_FUNCTION_AARCH64_DARWIN_RE),
"bpf": (scrub_asm_bpf, ASM_FUNCTION_BPF_RE),
"bpfel": (scrub_asm_bpf, ASM_FUNCTION_BPF_RE),
"bpfeb": (scrub_asm_bpf, ASM_FUNCTION_BPF_RE),
"hexagon": (scrub_asm_hexagon, ASM_FUNCTION_HEXAGON_RE),
"r600": (scrub_asm_amdgpu, ASM_FUNCTION_AMDGPU_RE),
"amdgcn": (scrub_asm_amdgpu, ASM_FUNCTION_AMDGPU_RE),
"arm": (scrub_asm_arm_eabi, ASM_FUNCTION_ARM_RE),
"arm64": (scrub_asm_arm_eabi, ASM_FUNCTION_AARCH64_RE),
"arm64e": (scrub_asm_arm_eabi, ASM_FUNCTION_AARCH64_DARWIN_RE),
"arm64ec": (scrub_asm_arm_eabi, ASM_FUNCTION_AARCH64_RE),
"arm64-apple-ios": (scrub_asm_arm_eabi, ASM_FUNCTION_AARCH64_DARWIN_RE),
"armv7-apple-ios": (scrub_asm_arm_eabi, ASM_FUNCTION_ARM_IOS_RE),
"armv7-apple-darwin": (scrub_asm_arm_eabi, ASM_FUNCTION_ARM_DARWIN_RE),
"thumb": (scrub_asm_arm_eabi, ASM_FUNCTION_ARM_RE),
"thumb-macho": (scrub_asm_arm_eabi, ASM_FUNCTION_ARM_MACHO_RE),
"thumbv5-macho": (scrub_asm_arm_eabi, ASM_FUNCTION_ARM_MACHO_RE),
"thumbv7s-apple-darwin": (scrub_asm_arm_eabi, ASM_FUNCTION_THUMBS_DARWIN_RE),
"thumbv7-apple-darwin": (scrub_asm_arm_eabi, ASM_FUNCTION_THUMB_DARWIN_RE),
"thumbv7-apple-ios": (scrub_asm_arm_eabi, ASM_FUNCTION_ARM_IOS_RE),
"m68k": (scrub_asm_m68k, ASM_FUNCTION_M68K_RE),
"mips": (scrub_asm_mips, ASM_FUNCTION_MIPS_RE),
"msp430": (scrub_asm_msp430, ASM_FUNCTION_MSP430_RE),
"avr": (scrub_asm_avr, ASM_FUNCTION_AVR_RE),
"ppc32": (scrub_asm_powerpc, ASM_FUNCTION_PPC_RE),
"ppc64": (scrub_asm_powerpc, ASM_FUNCTION_PPC_RE),
"powerpc": (scrub_asm_powerpc, ASM_FUNCTION_PPC_RE),
"riscv32": (scrub_asm_riscv, ASM_FUNCTION_RISCV_RE),
"riscv64": (scrub_asm_riscv, ASM_FUNCTION_RISCV_RE),
"lanai": (scrub_asm_lanai, ASM_FUNCTION_LANAI_RE),
"sparc": (scrub_asm_sparc, ASM_FUNCTION_SPARC_RE),
"s390x": (scrub_asm_systemz, ASM_FUNCTION_SYSTEMZ_RE),
"wasm32": (scrub_asm_wasm, ASM_FUNCTION_WASM_RE),
"wasm64": (scrub_asm_wasm, ASM_FUNCTION_WASM_RE),
"ve": (scrub_asm_ve, ASM_FUNCTION_VE_RE),
"csky": (scrub_asm_csky, ASM_FUNCTION_CSKY_RE),
"nvptx": (scrub_asm_nvptx, ASM_FUNCTION_NVPTX_RE),
"loongarch32": (scrub_asm_loongarch, ASM_FUNCTION_LOONGARCH_RE),
"loongarch64": (scrub_asm_loongarch, ASM_FUNCTION_LOONGARCH_RE),
}
handler = None
best_prefix = ""
for prefix, s in target_handlers.items():
if triple.startswith(prefix) and len(prefix) > len(best_prefix):
handler = s
best_prefix = prefix
if handler is None:
raise KeyError('Triple %r is not supported' % (triple))
if handler is None:
raise KeyError("Triple %r is not supported" % (triple))
return handler
return handler
##### Generator of assembly CHECK lines
def add_checks(output_lines, comment_marker, prefix_list, func_dict,
func_name, global_vars_seen_dict, is_filtered):
# Label format is based on ASM string.
check_label_format = '{} %s-LABEL: %s%s%s%s'.format(comment_marker)
return common.add_checks(output_lines, comment_marker, prefix_list, func_dict,
func_name, check_label_format, True, False, 1,
global_vars_seen_dict, is_filtered=is_filtered)
def add_checks(
output_lines,
comment_marker,
prefix_list,
func_dict,
func_name,
global_vars_seen_dict,
is_filtered,
):
# Label format is based on ASM string.
check_label_format = "{} %s-LABEL: %s%s%s%s".format(comment_marker)
return common.add_checks(
output_lines,
comment_marker,
prefix_list,
func_dict,
func_name,
check_label_format,
True,
False,
1,
global_vars_seen_dict,
is_filtered=is_filtered,
)

File diff suppressed because it is too large Load Diff

View File

@ -3,10 +3,12 @@ from . import common
import sys
if sys.version_info[0] > 2:
class string:
expandtabs = str.expandtabs
class string:
expandtabs = str.expandtabs
else:
import string
import string
# Support of isel debug checks
# RegEx: this is where the magic happens.
@ -15,43 +17,64 @@ else:
# TODO: add function prefix
ISEL_FUNCTION_DEFAULT_RE = re.compile(
r'Selected[\s]*selection[\s]*DAG:[\s]*%bb.0[\s]*\'(?P<func>.*?):[^\']*\'*\n'
r'(?P<body>.*?)\n'
r'Total[\s]*amount[\s]*of[\s]*phi[\s]*nodes[\s]*to[\s]*update:[\s]*[0-9]+',
flags=(re.M | re.S))
r"Selected[\s]*selection[\s]*DAG:[\s]*%bb.0[\s]*\'(?P<func>.*?):[^\']*\'*\n"
r"(?P<body>.*?)\n"
r"Total[\s]*amount[\s]*of[\s]*phi[\s]*nodes[\s]*to[\s]*update:[\s]*[0-9]+",
flags=(re.M | re.S),
)
def scrub_isel_default(isel, args):
# Scrub runs of whitespace out of the iSel debug output, but leave the leading
# whitespace in place.
isel = common.SCRUB_WHITESPACE_RE.sub(r' ', isel)
# Expand the tabs used for indentation.
isel = string.expandtabs(isel, 2)
# Strip trailing whitespace.
isel = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r'', isel)
return isel
# Scrub runs of whitespace out of the iSel debug output, but leave the leading
# whitespace in place.
isel = common.SCRUB_WHITESPACE_RE.sub(r" ", isel)
# Expand the tabs used for indentation.
isel = string.expandtabs(isel, 2)
# Strip trailing whitespace.
isel = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r"", isel)
return isel
def get_run_handler(triple):
target_handlers = {
}
handler = None
best_prefix = ''
for prefix, s in target_handlers.items():
if triple.startswith(prefix) and len(prefix) > len(best_prefix):
handler = s
best_prefix = prefix
target_handlers = {}
handler = None
best_prefix = ""
for prefix, s in target_handlers.items():
if triple.startswith(prefix) and len(prefix) > len(best_prefix):
handler = s
best_prefix = prefix
if handler is None:
common.debug('Using default handler.')
handler = (scrub_isel_default, ISEL_FUNCTION_DEFAULT_RE)
if handler is None:
common.debug("Using default handler.")
handler = (scrub_isel_default, ISEL_FUNCTION_DEFAULT_RE)
return handler
return handler
##### Generator of iSel CHECK lines
def add_checks(output_lines, comment_marker, prefix_list, func_dict, func_name,
global_vars_seen_dict, is_filtered):
# Label format is based on iSel string.
check_label_format = '{} %s-LABEL: %s%s%s%s'.format(comment_marker)
return common.add_checks(output_lines, comment_marker, prefix_list, func_dict,
func_name, check_label_format, True, False, 1,
global_vars_seen_dict, is_filtered=is_filtered)
def add_checks(
output_lines,
comment_marker,
prefix_list,
func_dict,
func_name,
global_vars_seen_dict,
is_filtered,
):
# Label format is based on iSel string.
check_label_format = "{} %s-LABEL: %s%s%s%s".format(comment_marker)
return common.add_checks(
output_lines,
comment_marker,
prefix_list,
func_dict,
func_name,
check_label_format,
True,
False,
1,
global_vars_seen_dict,
is_filtered=is_filtered,
)

View File

@ -57,11 +57,7 @@ FAILED = RED + "failed" + NORMAL
def find(dir, file_filter=None):
files = [
walkdir[0]+"/"+file
for walkdir in os.walk(dir)
for file in walkdir[2]
]
files = [walkdir[0] + "/" + file for walkdir in os.walk(dir) for file in walkdir[2]]
if file_filter is not None:
files = filter(files, file_filter)
return sorted(files)
@ -147,8 +143,10 @@ def check_bisect(choices, perform_test):
picks = dict(all_a)
for x in partition:
picks[x] = choice_map[x][1]
announce_test("checking %s [<=%d remaining]" %
(format_namelist(partition), max_remaining_steps))
announce_test(
"checking %s [<=%d remaining]"
% (format_namelist(partition), max_remaining_steps)
)
res = perform_test(picks)
if res is True:
known_good.update(partition)
@ -184,7 +182,7 @@ def extract_functions(file):
if marker != -1:
if in_function is not None:
warn("Missing end of function %s" % (in_function,))
funcname = line[marker + 19:-1]
funcname = line[marker + 19 : -1]
in_function = funcname
text = line
continue
@ -210,7 +208,7 @@ def replace_functions(source, dest, replacements):
if marker != -1:
if in_function is not None:
warn("Missing end of function %s" % (in_function,))
funcname = line[marker + 19:-1]
funcname = line[marker + 19 : -1]
in_function = funcname
replacement = replacements.get(in_function)
if replacement is not None:
@ -229,7 +227,10 @@ def replace_functions(source, dest, replacements):
def testrun(files):
linkline = "%s %s" % (LINKTEST, " ".join(files),)
linkline = "%s %s" % (
LINKTEST,
" ".join(files),
)
res = subprocess.call(linkline, shell=True)
if res != 0:
announce_result(FAILED + ": '%s' exitcode != 0" % LINKTEST)
@ -244,12 +245,13 @@ def prepare_files(gooddir, baddir, rspfile):
files_b = []
if rspfile is not None:
def get_basename(name):
# remove prefix
if name.startswith(gooddir):
return name[len(gooddir):]
return name[len(gooddir) :]
if name.startswith(baddir):
return name[len(baddir):]
return name[len(baddir) :]
assert False, ""
with open(rspfile, "r") as rf:
@ -269,15 +271,13 @@ def prepare_files(gooddir, baddir, rspfile):
for name in files_b:
basename = get_basename(name)
if basename not in basenames_a:
warn("There is no corresponding file to '%s' in %s" %
(name, gooddir))
warn("There is no corresponding file to '%s' in %s" % (name, gooddir))
choices = []
skipped = []
for name in files_a:
basename = get_basename(name)
if basename not in basenames_b:
warn("There is no corresponding file to '%s' in %s" %
(name, baddir))
warn("There is no corresponding file to '%s' in %s" % (name, baddir))
file_a = gooddir + "/" + basename
file_b = baddir + "/" + basename
@ -307,8 +307,7 @@ def prepare_files(gooddir, baddir, rspfile):
# If response file is used, create a temporary response file for the
# picked files.
if rspfile is not None:
with tempfile.NamedTemporaryFile('w', suffix='.rsp',
delete=False) as tf:
with tempfile.NamedTemporaryFile("w", suffix=".rsp", delete=False) as tf:
tf.write(" ".join(files))
tf.flush()
ret = testrun([tf.name])
@ -346,7 +345,7 @@ def prepare_functions(to_check, gooddir, goodfile, badfile):
if len(skipped) > 0:
info("Skipped (same content): %s" % format_namelist(skipped))
combined_file = '/tmp/combined2.s'
combined_file = "/tmp/combined2.s"
files = []
found_good_file = False
for c in files_good:
@ -362,21 +361,21 @@ def prepare_functions(to_check, gooddir, goodfile, badfile):
assert x == functions_a_map[name] or x == functions_b_map[name]
replace_functions(goodfile, combined_file, picks)
return testrun(files)
return perform_test, choices
def main():
parser = argparse.ArgumentParser()
parser.add_argument('--a', dest='dir_a', default='before')
parser.add_argument('--b', dest='dir_b', default='after')
parser.add_argument('--rsp', default=None)
parser.add_argument('--test', default='./link_test')
parser.add_argument('--insane', help='Skip sanity check',
action='store_true')
parser.add_argument('--seq',
help='Check sequentially instead of bisection',
action='store_true')
parser.add_argument('file', metavar='file', nargs='?')
parser.add_argument("--a", dest="dir_a", default="before")
parser.add_argument("--b", dest="dir_b", default="after")
parser.add_argument("--rsp", default=None)
parser.add_argument("--test", default="./link_test")
parser.add_argument("--insane", help="Skip sanity check", action="store_true")
parser.add_argument(
"--seq", help="Check sequentially instead of bisection", action="store_true"
)
parser.add_argument("file", metavar="file", nargs="?")
config = parser.parse_args()
gooddir = config.dir_a
@ -391,8 +390,9 @@ def main():
if config.file is not None:
goodfile = gooddir + "/" + config.file
badfile = baddir + "/" + config.file
perform_test, choices = prepare_functions(config.file, gooddir,
goodfile, badfile)
perform_test, choices = prepare_functions(
config.file, gooddir, goodfile, badfile
)
else:
perform_test, choices = prepare_files(gooddir, baddir, rspfile)
@ -423,5 +423,5 @@ def main():
stderr.write("Could not identify failing parts?!?")
if __name__ == '__main__':
if __name__ == "__main__":
main()

View File

@ -1,55 +1,59 @@
#!/usr/bin/env python3
import re, sys
def fix_string(s):
TYPE = re.compile('\s*(i[0-9]+|float|double|x86_fp80|fp128|ppc_fp128|\[\[.*?\]\]|\[2 x \[\[[A-Z_0-9]+\]\]\]|<.*?>|{.*?}|\[[0-9]+ x .*?\]|%["a-z:A-Z0-9._]+({{.*?}})?|%{{.*?}}|{{.*?}}|\[\[.*?\]\])(\s*(\*|addrspace\(.*?\)|dereferenceable\(.*?\)|byval\(.*?\)|sret|zeroext|inreg|returned|signext|nocapture|align \d+|swiftself|swifterror|readonly|noalias|inalloca|nocapture))*\s*')
TYPE = re.compile(
'\s*(i[0-9]+|float|double|x86_fp80|fp128|ppc_fp128|\[\[.*?\]\]|\[2 x \[\[[A-Z_0-9]+\]\]\]|<.*?>|{.*?}|\[[0-9]+ x .*?\]|%["a-z:A-Z0-9._]+({{.*?}})?|%{{.*?}}|{{.*?}}|\[\[.*?\]\])(\s*(\*|addrspace\(.*?\)|dereferenceable\(.*?\)|byval\(.*?\)|sret|zeroext|inreg|returned|signext|nocapture|align \d+|swiftself|swifterror|readonly|noalias|inalloca|nocapture))*\s*'
)
counter = 0
if 'i32{{.*}}' in s:
if "i32{{.*}}" in s:
counter = 1
at_pos = s.find('@')
at_pos = s.find("@")
if at_pos == -1:
at_pos = 0
annoying_pos = s.find('{{[^(]+}}')
annoying_pos = s.find("{{[^(]+}}")
if annoying_pos != -1:
at_pos = annoying_pos + 9
paren_pos = s.find('(', at_pos)
paren_pos = s.find("(", at_pos)
if paren_pos == -1:
return s
res = s[:paren_pos+1]
s = s[paren_pos+1:]
res = s[: paren_pos + 1]
s = s[paren_pos + 1 :]
m = TYPE.match(s)
while m:
res += m.group()
s = s[m.end():]
if s.startswith(',') or s.startswith(')'):
res += f' %{counter}'
s = s[m.end() :]
if s.startswith(",") or s.startswith(")"):
res += f" %{counter}"
counter += 1
next_arg = s.find(',')
next_arg = s.find(",")
if next_arg == -1:
break
res += s[:next_arg+1]
s = s[next_arg+1:]
res += s[: next_arg + 1]
s = s[next_arg + 1 :]
m = TYPE.match(s)
return res+s
return res + s
def process_file(contents):
PREFIX = re.compile(r'check-prefix(es)?(=|\s+)([a-zA-Z0-9,]+)')
check_prefixes = ['CHECK']
result = ''
for line in contents.split('\n'):
if 'FileCheck' in line:
PREFIX = re.compile(r"check-prefix(es)?(=|\s+)([a-zA-Z0-9,]+)")
check_prefixes = ["CHECK"]
result = ""
for line in contents.split("\n"):
if "FileCheck" in line:
m = PREFIX.search(line)
if m:
check_prefixes.extend(m.group(3).split(','))
check_prefixes.extend(m.group(3).split(","))
found_check = False
for prefix in check_prefixes:
@ -57,26 +61,28 @@ def process_file(contents):
found_check = True
break
if not found_check or 'define' not in line:
result += line + '\n'
if not found_check or "define" not in line:
result += line + "\n"
continue
# We have a check for a function definition. Number the args.
line = fix_string(line)
result += line + '\n'
result += line + "\n"
return result
def main():
print(f'Processing {sys.argv[1]}')
print(f"Processing {sys.argv[1]}")
f = open(sys.argv[1])
content = f.read()
f.close()
content = process_file(content)
f = open(sys.argv[1], 'w')
f = open(sys.argv[1], "w")
f.write(content)
f.close()
if __name__ == '__main__':
if __name__ == "__main__":
main()

View File

@ -24,40 +24,38 @@ def log(msg):
def hr():
log('-' * 50)
log("-" * 50)
def log_err(msg):
print('ERROR: {}'.format(msg), file=sys.stderr)
print("ERROR: {}".format(msg), file=sys.stderr)
def check_path(path):
if not os.path.exists(path):
log_err('{} does not exist.'.format(path))
log_err("{} does not exist.".format(path))
raise
return path
def check_bin(build_dir, bin_name):
file_name = '{}/bin/{}'.format(build_dir, bin_name)
file_name = "{}/bin/{}".format(build_dir, bin_name)
return check_path(file_name)
def run_llc(llc, irfile):
pr = subprocess.Popen([llc,
'-o',
'-',
'-global-isel',
'-pass-remarks-missed=gisel',
irfile],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
pr = subprocess.Popen(
[llc, "-o", "-", "-global-isel", "-pass-remarks-missed=gisel", irfile],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
out, err = pr.communicate()
res = pr.wait()
if res == 0:
return 0
re_err = re.compile(
r'LLVM ERROR: ([a-z\s]+):.*(G_INTRINSIC[_A-Z]* <intrinsic:@[a-zA-Z0-9\.]+>|G_[A-Z_]+)')
r"LLVM ERROR: ([a-z\s]+):.*(G_INTRINSIC[_A-Z]* <intrinsic:@[a-zA-Z0-9\.]+>|G_[A-Z_]+)"
)
match = re_err.match(err)
if not match:
return 0
@ -66,13 +64,18 @@ def run_llc(llc, irfile):
def run_bugpoint(bugpoint_bin, llc_bin, opt_bin, tmp, ir_file):
compileCmd = '-compile-command={} -c {} {}'.format(
os.path.realpath(__file__), llc_bin, tmp)
pr = subprocess.Popen([bugpoint_bin,
'-compile-custom',
compileCmd,
'-opt-command={}'.format(opt_bin),
ir_file])
compileCmd = "-compile-command={} -c {} {}".format(
os.path.realpath(__file__), llc_bin, tmp
)
pr = subprocess.Popen(
[
bugpoint_bin,
"-compile-custom",
compileCmd,
"-opt-command={}".format(opt_bin),
ir_file,
]
)
res = pr.wait()
if res != 0:
log_err("Unable to reduce the test.")
@ -83,13 +86,13 @@ def run_bugpoint_check():
path_to_llc = sys.argv[2]
path_to_err = sys.argv[3]
path_to_ir = sys.argv[4]
with open(path_to_err, 'r') as f:
with open(path_to_err, "r") as f:
err = f.read()
res = run_llc(path_to_llc, path_to_ir)
if res == 0:
return 0
log('GlobalISed failed, {}: {}'.format(res[0], res[1]))
if res != err.split(';'):
log("GlobalISed failed, {}: {}".format(res[0], res[1]))
if res != err.split(";"):
return 0
else:
return 1
@ -97,50 +100,53 @@ def run_bugpoint_check():
def main():
# Check if this is called by bugpoint.
if len(sys.argv) == 5 and sys.argv[1] == '-c':
if len(sys.argv) == 5 and sys.argv[1] == "-c":
sys.exit(run_bugpoint_check())
# Parse arguments.
parser = argparse.ArgumentParser(
description=__doc__, formatter_class=argparse.RawTextHelpFormatter)
parser.add_argument('BuildDir', help="Path to LLVM build directory")
parser.add_argument('IRFile', help="Path to the input IR file")
description=__doc__, formatter_class=argparse.RawTextHelpFormatter
)
parser.add_argument("BuildDir", help="Path to LLVM build directory")
parser.add_argument("IRFile", help="Path to the input IR file")
args = parser.parse_args()
# Check if the binaries exist.
build_dir = check_path(args.BuildDir)
ir_file = check_path(args.IRFile)
llc_bin = check_bin(build_dir, 'llc')
opt_bin = check_bin(build_dir, 'opt')
bugpoint_bin = check_bin(build_dir, 'bugpoint')
llc_bin = check_bin(build_dir, "llc")
opt_bin = check_bin(build_dir, "opt")
bugpoint_bin = check_bin(build_dir, "bugpoint")
# Run llc to see if GlobalISel fails.
log('Running llc...')
log("Running llc...")
res = run_llc(llc_bin, ir_file)
if res == 0:
log_err("Expected failure")
raise
hr()
log('GlobalISel failed, {}: {}.'.format(res[0], res[1]))
log("GlobalISel failed, {}: {}.".format(res[0], res[1]))
tmp = tempfile.NamedTemporaryFile()
log('Writing error to {} for bugpoint.'.format(tmp.name))
tmp.write(';'.join(res))
log("Writing error to {} for bugpoint.".format(tmp.name))
tmp.write(";".join(res))
tmp.flush()
hr()
# Run bugpoint.
log('Running bugpoint...')
log("Running bugpoint...")
run_bugpoint(bugpoint_bin, llc_bin, opt_bin, tmp.name, ir_file)
hr()
log('Done!')
log("Done!")
hr()
output_file = 'bugpoint-reduced-simplified.bc'
log('Run llvm-dis to disassemble the output:')
log('$ {}/bin/llvm-dis -o - {}'.format(build_dir, output_file))
log('Run llc to reproduce the problem:')
log('$ {}/bin/llc -o - -global-isel '
'-pass-remarks-missed=gisel {}'.format(build_dir, output_file))
output_file = "bugpoint-reduced-simplified.bc"
log("Run llvm-dis to disassemble the output:")
log("$ {}/bin/llvm-dis -o - {}".format(build_dir, output_file))
log("Run llc to reproduce the problem:")
log(
"$ {}/bin/llc -o - -global-isel "
"-pass-remarks-missed=gisel {}".format(build_dir, output_file)
)
if __name__ == '__main__':
if __name__ == "__main__":
main()

View File

@ -58,6 +58,7 @@ import argparse
import subprocess
import pygraphviz
def toposort(g):
"""Topologically sort a graph.
@ -88,7 +89,8 @@ def toposort(g):
# If that counter reaches zero, w is ready to output.
ready.add(w)
def ancestors(g, translate = lambda x: x):
def ancestors(g, translate=lambda x: x):
"""Form the set of ancestors for each vertex of a graph.
The input g is a pygraphviz graph object representing a DAG. The function
@ -107,7 +109,7 @@ def ancestors(g, translate = lambda x: x):
vm = translate(v)
# Make up a[v], based on a[predecessors of v].
a[v] = {vm} # include v itself
a[v] = {vm} # include v itself
for w in g.in_neighbors(v):
a[v].update(a[w])
@ -115,14 +117,16 @@ def ancestors(g, translate = lambda x: x):
# doesn't get the trivial dependency of v on itself.
yield vm, a[v].difference({vm})
def main():
parser = argparse.ArgumentParser(
description='Find missing formal dependencies on generated include '
'files in a build.ninja file.')
parser.add_argument("-C", "--build-dir",
help="Build directory (default cwd)")
parser.add_argument("-f", "--build-file",
help="Build directory (default build.ninja)")
description="Find missing formal dependencies on generated include "
"files in a build.ninja file."
)
parser.add_argument("-C", "--build-dir", help="Build directory (default cwd)")
parser.add_argument(
"-f", "--build-file", help="Build directory (default build.ninja)"
)
args = parser.parse_args()
errs = 0
@ -134,8 +138,9 @@ def main():
ninja_prefix.extend(["-f", args.build_file])
# Get the formal dependency graph and decode it using pygraphviz.
g = pygraphviz.AGraph(subprocess.check_output(
ninja_prefix + ["-t", "graph"]).decode("UTF-8"))
g = pygraphviz.AGraph(
subprocess.check_output(ninja_prefix + ["-t", "graph"]).decode("UTF-8")
)
# Helper function to ask for the label of a vertex, which is where ninja's
# Graphviz output keeps the actual file name of the target.
@ -153,8 +158,11 @@ def main():
# Fetch the cached dependency data and check it against our formal ancestry
# data.
currtarget = None
for line in (subprocess.check_output(ninja_prefix + ["-t", "deps"])
.decode("UTF-8").splitlines()):
for line in (
subprocess.check_output(ninja_prefix + ["-t", "deps"])
.decode("UTF-8")
.splitlines()
):
# ninja -t deps output consists of stanzas of the following form,
# separated by a blank line:
#
@ -176,10 +184,15 @@ def main():
# cache is not cleared when build.ninja changes, so it can contain
# stale data from targets that existed only in past builds in the
# same directory.
if (dep in targets and currtarget in deps and
dep not in deps[currtarget]):
print("error:", currtarget, "requires", dep,
"but has no dependency on it", file=sys.stderr)
if dep in targets and currtarget in deps and dep not in deps[currtarget]:
print(
"error:",
currtarget,
"requires",
dep,
"but has no dependency on it",
file=sys.stderr,
)
errs += 1
elif ":" in line:
currtarget = line.split(":", 1)[0]
@ -187,5 +200,6 @@ def main():
if errs:
sys.exit("{:d} errors found".format(errs))
if __name__ == '__main__':
if __name__ == "__main__":
main()

View File

@ -1,9 +1,9 @@
#!/usr/bin/env python
# Given a -print-before-all and/or -print-after-all -print-module-scope log from
# an opt invocation, chunk it into a series of individual IR files, one for each
# pass invocation. If the log ends with an obvious stack trace, try to split off
# a separate "crashinfo.txt" file leaving only the valid input IR in the last
# Given a -print-before-all and/or -print-after-all -print-module-scope log from
# an opt invocation, chunk it into a series of individual IR files, one for each
# pass invocation. If the log ends with an obvious stack trace, try to split off
# a separate "crashinfo.txt" file leaving only the valid input IR in the last
# chunk. Files are written to current working directory.
import sys
@ -14,8 +14,9 @@ chunk_id = 0
# This function gets the pass name from the following line:
# *** IR Dump Before/After PASS_NAME... ***
def get_pass_name(line, prefix):
short_line = line[line.find(prefix) + len(prefix) + 1:]
return re.split(' |<', short_line)[0]
short_line = line[line.find(prefix) + len(prefix) + 1 :]
return re.split(" |<", short_line)[0]
def print_chunk(lines, prefix, pass_name):
global chunk_id
@ -25,6 +26,7 @@ def print_chunk(lines, prefix, pass_name):
with open(fname, "w") as f:
f.writelines(lines)
is_dump = False
cur = []
for line in sys.stdin:

View File

@ -36,7 +36,7 @@ import sys
# in to build more things, if you'd like.
def _run_benchmark(env, out_dir, include_debug_info):
"""The 'benchmark' we run to generate profile data."""
target_dir = env.output_subdir('instrumentation_run')
target_dir = env.output_subdir("instrumentation_run")
# `check-llvm` and `check-clang` are cheap ways to increase coverage. The
# former lets us touch on the non-x86 backends a bit if configured, and the
@ -44,34 +44,34 @@ def _run_benchmark(env, out_dir, include_debug_info):
# paths a fair amount, though the `if (stuff_is_broken) { diag() ... }`
# branches should still heavily be weighted in the not-taken direction,
# since we built all of LLVM/etc).
_build_things_in(env, out_dir, what=['check-llvm', 'check-clang'])
_build_things_in(env, out_dir, what=["check-llvm", "check-clang"])
# Building tblgen gets us coverage; don't skip it. (out_dir may also not
# have them anyway, but that's less of an issue)
cmake = _get_cmake_invocation_for_bootstrap_from(
env, out_dir, skip_tablegens=False)
cmake = _get_cmake_invocation_for_bootstrap_from(env, out_dir, skip_tablegens=False)
if include_debug_info:
cmake.add_flag('CMAKE_BUILD_TYPE', 'RelWithDebInfo')
cmake.add_flag("CMAKE_BUILD_TYPE", "RelWithDebInfo")
_run_fresh_cmake(env, cmake, target_dir)
# Just build all the things. The more data we have, the better.
_build_things_in(env, target_dir, what=['all'])
_build_things_in(env, target_dir, what=["all"])
### Script
class CmakeInvocation:
_cflags = ['CMAKE_C_FLAGS', 'CMAKE_CXX_FLAGS']
_cflags = ["CMAKE_C_FLAGS", "CMAKE_CXX_FLAGS"]
_ldflags = [
'CMAKE_EXE_LINKER_FLAGS',
'CMAKE_MODULE_LINKER_FLAGS',
'CMAKE_SHARED_LINKER_FLAGS',
"CMAKE_EXE_LINKER_FLAGS",
"CMAKE_MODULE_LINKER_FLAGS",
"CMAKE_SHARED_LINKER_FLAGS",
]
def __init__(self, cmake, maker, cmake_dir):
self._prefix = [cmake, '-G', maker, cmake_dir]
self._prefix = [cmake, "-G", maker, cmake_dir]
# Map of str -> (list|str).
self._flags = {}
@ -92,7 +92,7 @@ class CmakeInvocation:
return
if not allow_overwrites:
raise ValueError('Invalid overwrite of %s requested' % key)
raise ValueError("Invalid overwrite of %s requested" % key)
self._flags[key] = value
@ -115,18 +115,17 @@ class CmakeInvocation:
# nothing to add, don't.
if not value:
continue
value = ' '.join(value)
value = " ".join(value)
arg = '-D' + key
if value != '':
arg += '=' + value
arg = "-D" + key
if value != "":
arg += "=" + value
args.append(arg)
return args
class Env:
def __init__(self, llvm_dir, use_make, output_dir, default_cmake_args,
dry_run):
def __init__(self, llvm_dir, use_make, output_dir, default_cmake_args, dry_run):
self.llvm_dir = llvm_dir
self.use_make = use_make
self.output_dir = output_dir
@ -137,35 +136,30 @@ class Env:
return self.default_cmake_args.items()
def get_cmake_maker(self):
return 'Ninja' if not self.use_make else 'Unix Makefiles'
return "Ninja" if not self.use_make else "Unix Makefiles"
def get_make_command(self):
if self.use_make:
return ['make', '-j{}'.format(multiprocessing.cpu_count())]
return ['ninja']
return ["make", "-j{}".format(multiprocessing.cpu_count())]
return ["ninja"]
def output_subdir(self, name):
return os.path.join(self.output_dir, name)
def has_llvm_subproject(self, name):
if name == 'compiler-rt':
subdir = '../compiler-rt'
elif name == 'clang':
subdir = '../clang'
if name == "compiler-rt":
subdir = "../compiler-rt"
elif name == "clang":
subdir = "../clang"
else:
raise ValueError('Unknown subproject: %s' % name)
raise ValueError("Unknown subproject: %s" % name)
return os.path.isdir(os.path.join(self.llvm_dir, subdir))
# Note that we don't allow capturing stdout/stderr. This works quite nicely
# with dry_run.
def run_command(self,
cmd,
cwd=None,
check=False,
silent_unless_error=False):
print(
'Running `%s` in %s' % (cmd, shlex.quote(cwd or os.getcwd())))
def run_command(self, cmd, cwd=None, check=False, silent_unless_error=False):
print("Running `%s` in %s" % (cmd, shlex.quote(cwd or os.getcwd())))
if self.dry_run:
return
@ -178,11 +172,8 @@ class Env:
# Don't use subprocess.run because it's >= py3.5 only, and it's not too
# much extra effort to get what it gives us anyway.
popen = subprocess.Popen(
cmd,
stdin=subprocess.DEVNULL,
stdout=stdout,
stderr=stderr,
cwd=cwd)
cmd, stdin=subprocess.DEVNULL, stdout=stdout, stderr=stderr, cwd=cwd
)
stdout, _ = popen.communicate()
return_code = popen.wait(timeout=0)
@ -190,32 +181,33 @@ class Env:
return
if silent_unless_error:
print(stdout.decode('utf-8', 'ignore'))
print(stdout.decode("utf-8", "ignore"))
if check:
raise subprocess.CalledProcessError(
returncode=return_code, cmd=cmd, output=stdout, stderr=None)
returncode=return_code, cmd=cmd, output=stdout, stderr=None
)
def _get_default_cmake_invocation(env):
inv = CmakeInvocation(
cmake='cmake', maker=env.get_cmake_maker(), cmake_dir=env.llvm_dir)
cmake="cmake", maker=env.get_cmake_maker(), cmake_dir=env.llvm_dir
)
for key, value in env.get_default_cmake_args_kv():
inv.add_new_flag(key, value)
return inv
def _get_cmake_invocation_for_bootstrap_from(env, out_dir,
skip_tablegens=True):
clang = os.path.join(out_dir, 'bin', 'clang')
def _get_cmake_invocation_for_bootstrap_from(env, out_dir, skip_tablegens=True):
clang = os.path.join(out_dir, "bin", "clang")
cmake = _get_default_cmake_invocation(env)
cmake.add_new_flag('CMAKE_C_COMPILER', clang)
cmake.add_new_flag('CMAKE_CXX_COMPILER', clang + '++')
cmake.add_new_flag("CMAKE_C_COMPILER", clang)
cmake.add_new_flag("CMAKE_CXX_COMPILER", clang + "++")
# We often get no value out of building new tblgens; the previous build
# should have them. It's still correct to build them, just slower.
def add_tablegen(key, binary):
path = os.path.join(out_dir, 'bin', binary)
path = os.path.join(out_dir, "bin", binary)
# Check that this exists, since the user's allowed to specify their own
# stage1 directory (which is generally where we'll source everything
@ -224,8 +216,8 @@ def _get_cmake_invocation_for_bootstrap_from(env, out_dir,
cmake.add_new_flag(key, path)
if skip_tablegens:
add_tablegen('LLVM_TABLEGEN', 'llvm-tblgen')
add_tablegen('CLANG_TABLEGEN', 'clang-tblgen')
add_tablegen("LLVM_TABLEGEN", "llvm-tblgen")
add_tablegen("CLANG_TABLEGEN", "clang-tblgen")
return cmake
@ -245,146 +237,160 @@ def _run_fresh_cmake(env, cmake, target_dir):
os.makedirs(target_dir, mode=0o755)
cmake_args = cmake.to_args()
env.run_command(
cmake_args, cwd=target_dir, check=True, silent_unless_error=True)
env.run_command(cmake_args, cwd=target_dir, check=True, silent_unless_error=True)
def _build_stage1_clang(env):
target_dir = env.output_subdir('stage1')
target_dir = env.output_subdir("stage1")
cmake = _get_default_cmake_invocation(env)
_run_fresh_cmake(env, cmake, target_dir)
_build_things_in(env, target_dir, what=['clang', 'llvm-profdata', 'profile'])
_build_things_in(env, target_dir, what=["clang", "llvm-profdata", "profile"])
return target_dir
def _generate_instrumented_clang_profile(env, stage1_dir, profile_dir,
output_file):
llvm_profdata = os.path.join(stage1_dir, 'bin', 'llvm-profdata')
def _generate_instrumented_clang_profile(env, stage1_dir, profile_dir, output_file):
llvm_profdata = os.path.join(stage1_dir, "bin", "llvm-profdata")
if env.dry_run:
profiles = [os.path.join(profile_dir, '*.profraw')]
profiles = [os.path.join(profile_dir, "*.profraw")]
else:
profiles = [
os.path.join(profile_dir, f) for f in os.listdir(profile_dir)
if f.endswith('.profraw')
os.path.join(profile_dir, f)
for f in os.listdir(profile_dir)
if f.endswith(".profraw")
]
cmd = [llvm_profdata, 'merge', '-output=' + output_file] + profiles
cmd = [llvm_profdata, "merge", "-output=" + output_file] + profiles
env.run_command(cmd, check=True)
def _build_instrumented_clang(env, stage1_dir):
assert os.path.isabs(stage1_dir)
target_dir = os.path.join(env.output_dir, 'instrumented')
target_dir = os.path.join(env.output_dir, "instrumented")
cmake = _get_cmake_invocation_for_bootstrap_from(env, stage1_dir)
cmake.add_new_flag('LLVM_BUILD_INSTRUMENTED', 'IR')
cmake.add_new_flag("LLVM_BUILD_INSTRUMENTED", "IR")
# libcxx's configure step messes with our link order: we'll link
# libclang_rt.profile after libgcc, and the former requires atexit from the
# latter. So, configure checks fail.
#
# Since we don't need libcxx or compiler-rt anyway, just disable them.
cmake.add_new_flag('LLVM_BUILD_RUNTIME', 'No')
cmake.add_new_flag("LLVM_BUILD_RUNTIME", "No")
_run_fresh_cmake(env, cmake, target_dir)
_build_things_in(env, target_dir, what=['clang', 'lld'])
_build_things_in(env, target_dir, what=["clang", "lld"])
profiles_dir = os.path.join(target_dir, 'profiles')
profiles_dir = os.path.join(target_dir, "profiles")
return target_dir, profiles_dir
def _build_optimized_clang(env, stage1_dir, profdata_file):
if not env.dry_run and not os.path.exists(profdata_file):
raise ValueError('Looks like the profdata file at %s doesn\'t exist' %
profdata_file)
raise ValueError(
"Looks like the profdata file at %s doesn't exist" % profdata_file
)
target_dir = os.path.join(env.output_dir, 'optimized')
target_dir = os.path.join(env.output_dir, "optimized")
cmake = _get_cmake_invocation_for_bootstrap_from(env, stage1_dir)
cmake.add_new_flag('LLVM_PROFDATA_FILE', os.path.abspath(profdata_file))
cmake.add_new_flag("LLVM_PROFDATA_FILE", os.path.abspath(profdata_file))
# We'll get complaints about hash mismatches in `main` in tools/etc. Ignore
# it.
cmake.add_cflags(['-Wno-backend-plugin'])
cmake.add_cflags(["-Wno-backend-plugin"])
_run_fresh_cmake(env, cmake, target_dir)
_build_things_in(env, target_dir, what=['clang'])
_build_things_in(env, target_dir, what=["clang"])
return target_dir
Args = collections.namedtuple('Args', [
'do_optimized_build',
'include_debug_info',
'profile_location',
'stage1_dir',
])
Args = collections.namedtuple(
"Args",
[
"do_optimized_build",
"include_debug_info",
"profile_location",
"stage1_dir",
],
)
def _parse_args():
parser = argparse.ArgumentParser(
description='Builds LLVM and Clang with instrumentation, collects '
'instrumentation profiles for them, and (optionally) builds things '
'with these PGO profiles. By default, it\'s assumed that you\'re '
'running this from your LLVM root, and all build artifacts will be '
'saved to $PWD/out.')
description="Builds LLVM and Clang with instrumentation, collects "
"instrumentation profiles for them, and (optionally) builds things "
"with these PGO profiles. By default, it's assumed that you're "
"running this from your LLVM root, and all build artifacts will be "
"saved to $PWD/out."
)
parser.add_argument(
'--cmake-extra-arg',
action='append',
"--cmake-extra-arg",
action="append",
default=[],
help='an extra arg to pass to all cmake invocations. Note that this '
'is interpreted as a -D argument, e.g. --cmake-extra-arg FOO=BAR will '
'be passed as -DFOO=BAR. This may be specified multiple times.')
help="an extra arg to pass to all cmake invocations. Note that this "
"is interpreted as a -D argument, e.g. --cmake-extra-arg FOO=BAR will "
"be passed as -DFOO=BAR. This may be specified multiple times.",
)
parser.add_argument(
'--dry-run',
action='store_true',
help='print commands instead of running them')
"--dry-run", action="store_true", help="print commands instead of running them"
)
parser.add_argument(
'--llvm-dir',
default='.',
help='directory containing an LLVM checkout (default: $PWD)')
"--llvm-dir",
default=".",
help="directory containing an LLVM checkout (default: $PWD)",
)
parser.add_argument(
'--no-optimized-build',
action='store_true',
help='disable the final, PGO-optimized build')
"--no-optimized-build",
action="store_true",
help="disable the final, PGO-optimized build",
)
parser.add_argument(
'--out-dir',
help='directory to write artifacts to (default: $llvm_dir/out)')
"--out-dir", help="directory to write artifacts to (default: $llvm_dir/out)"
)
parser.add_argument(
'--profile-output',
help='where to output the profile (default is $out/pgo_profile.prof)')
"--profile-output",
help="where to output the profile (default is $out/pgo_profile.prof)",
)
parser.add_argument(
'--stage1-dir',
help='instead of having an initial build of everything, use the given '
'directory. It is expected that this directory will have clang, '
'llvm-profdata, and the appropriate libclang_rt.profile already built')
"--stage1-dir",
help="instead of having an initial build of everything, use the given "
"directory. It is expected that this directory will have clang, "
"llvm-profdata, and the appropriate libclang_rt.profile already built",
)
parser.add_argument(
'--use-debug-info-in-benchmark',
action='store_true',
help='use a regular build instead of RelWithDebInfo in the benchmark. '
'This increases benchmark execution time and disk space requirements, '
'but gives more coverage over debuginfo bits in LLVM and clang.')
"--use-debug-info-in-benchmark",
action="store_true",
help="use a regular build instead of RelWithDebInfo in the benchmark. "
"This increases benchmark execution time and disk space requirements, "
"but gives more coverage over debuginfo bits in LLVM and clang.",
)
parser.add_argument(
'--use-make',
action='store_true',
default=shutil.which('ninja') is None,
help='use Makefiles instead of ninja')
"--use-make",
action="store_true",
default=shutil.which("ninja") is None,
help="use Makefiles instead of ninja",
)
args = parser.parse_args()
llvm_dir = os.path.abspath(args.llvm_dir)
if args.out_dir is None:
output_dir = os.path.join(llvm_dir, 'out')
output_dir = os.path.join(llvm_dir, "out")
else:
output_dir = os.path.abspath(args.out_dir)
extra_args = {'CMAKE_BUILD_TYPE': 'Release',
'LLVM_ENABLE_PROJECTS': 'clang;compiler-rt;lld'}
extra_args = {
"CMAKE_BUILD_TYPE": "Release",
"LLVM_ENABLE_PROJECTS": "clang;compiler-rt;lld",
}
for arg in args.cmake_extra_arg:
if arg.startswith('-D'):
if arg.startswith("-D"):
arg = arg[2:]
elif arg.startswith('-'):
raise ValueError('Unknown not- -D arg encountered; you may need '
'to tweak the source...')
split = arg.split('=', 1)
elif arg.startswith("-"):
raise ValueError(
"Unknown not- -D arg encountered; you may need "
"to tweak the source..."
)
split = arg.split("=", 1)
if len(split) == 1:
key, val = split[0], ''
key, val = split[0], ""
else:
key, val = split
extra_args[key] = val
@ -400,7 +406,7 @@ def _parse_args():
if args.profile_output is not None:
profile_location = args.profile_output
else:
profile_location = os.path.join(env.output_dir, 'pgo_profile.prof')
profile_location = os.path.join(env.output_dir, "pgo_profile.prof")
result_args = Args(
do_optimized_build=not args.no_optimized_build,
@ -419,26 +425,26 @@ def _looks_like_llvm_dir(directory):
contents = set(os.listdir(directory))
expected_contents = [
'CODE_OWNERS.TXT',
'cmake',
'docs',
'include',
'utils',
"CODE_OWNERS.TXT",
"cmake",
"docs",
"include",
"utils",
]
if not all(c in contents for c in expected_contents):
return False
try:
include_listing = os.listdir(os.path.join(directory, 'include'))
include_listing = os.listdir(os.path.join(directory, "include"))
except NotADirectoryError:
return False
return 'llvm' in include_listing
return "llvm" in include_listing
def _die(*args, **kwargs):
kwargs['file'] = sys.stderr
kwargs["file"] = sys.stderr
print(*args, **kwargs)
sys.exit(1)
@ -447,37 +453,36 @@ def _main():
env, args = _parse_args()
if not _looks_like_llvm_dir(env.llvm_dir):
_die('Looks like %s isn\'t an LLVM directory; please see --help' %
env.llvm_dir)
if not env.has_llvm_subproject('clang'):
_die('Need a clang checkout at tools/clang')
if not env.has_llvm_subproject('compiler-rt'):
_die('Need a compiler-rt checkout at projects/compiler-rt')
_die("Looks like %s isn't an LLVM directory; please see --help" % env.llvm_dir)
if not env.has_llvm_subproject("clang"):
_die("Need a clang checkout at tools/clang")
if not env.has_llvm_subproject("compiler-rt"):
_die("Need a compiler-rt checkout at projects/compiler-rt")
def status(*args):
print(*args, file=sys.stderr)
if args.stage1_dir is None:
status('*** Building stage1 clang...')
status("*** Building stage1 clang...")
stage1_out = _build_stage1_clang(env)
else:
stage1_out = args.stage1_dir
status('*** Building instrumented clang...')
status("*** Building instrumented clang...")
instrumented_out, profile_dir = _build_instrumented_clang(env, stage1_out)
status('*** Running profdata benchmarks...')
status("*** Running profdata benchmarks...")
_run_benchmark(env, instrumented_out, args.include_debug_info)
status('*** Generating profile...')
_generate_instrumented_clang_profile(env, stage1_out, profile_dir,
args.profile_location)
status("*** Generating profile...")
_generate_instrumented_clang_profile(
env, stage1_out, profile_dir, args.profile_location
)
print('Final profile:', args.profile_location)
print("Final profile:", args.profile_location)
if args.do_optimized_build:
status('*** Building PGO-optimized binaries...')
optimized_out = _build_optimized_clang(env, stage1_out,
args.profile_location)
print('Final build directory:', optimized_out)
status("*** Building PGO-optimized binaries...")
optimized_out = _build_optimized_clang(env, stage1_out, args.profile_location)
print("Final build directory:", optimized_out)
if __name__ == '__main__':
if __name__ == "__main__":
_main()

View File

@ -34,21 +34,23 @@ import re
def main():
parser = argparse.ArgumentParser(
description='Convert constraint log to script to verify using Z3.')
parser.add_argument('log_file', metavar='log', type=str,
help='constraint-system log file')
description="Convert constraint log to script to verify using Z3."
)
parser.add_argument(
"log_file", metavar="log", type=str, help="constraint-system log file"
)
args = parser.parse_args()
content = ''
with open(args.log_file, 'rt') as f:
content = ""
with open(args.log_file, "rt") as f:
content = f.read()
groups = content.split('---')
var_re = re.compile('x\d+')
groups = content.split("---")
var_re = re.compile("x\d+")
print('from z3 import *')
print("from z3 import *")
for group in groups:
constraints = [g.strip() for g in group.split('\n') if g.strip() != '']
constraints = [g.strip() for g in group.split("\n") if g.strip() != ""]
variables = set()
for c in constraints[:-1]:
for m in var_re.finditer(c):
@ -57,13 +59,13 @@ def main():
continue
for v in variables:
print('{} = Int("{}")'.format(v, v))
print('s = Solver()')
print("s = Solver()")
for c in constraints[:-1]:
print('s.add({})'.format(c))
print("s.add({})".format(c))
expected = constraints[-1].strip()
print('assert(s.check() == {})'.format(expected))
print("assert(s.check() == {})".format(expected))
print('print("all checks passed")')
if __name__ == '__main__':
if __name__ == "__main__":
main()

View File

@ -13,33 +13,37 @@ really behaving linearly.
from __future__ import print_function
import argparse
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('rungs', type=int,
help="Number of ladder rungs. Must be a multiple of 2")
args = parser.parse_args()
if (args.rungs % 2) != 0:
print("Rungs must be a multiple of 2")
return
print("int ladder(int *foo, int *bar, int x) {")
rung1 = range(0, args.rungs, 2)
rung2 = range(1, args.rungs, 2)
for i in rung1:
print("rung1%d:" % i)
print("*foo = x++;")
if i != rung1[-1]:
print("if (*bar) goto rung1%d;" % (i+2))
print("else goto rung2%d;" % (i+1))
else:
print("goto rung2%d;" % (i+1))
for i in rung2:
print("rung2%d:" % i)
print("*foo = x++;")
if i != rung2[-1]:
print("goto rung2%d;" % (i+2))
else:
print("return *foo;")
print("}")
if __name__ == '__main__':
main()
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
"rungs", type=int, help="Number of ladder rungs. Must be a multiple of 2"
)
args = parser.parse_args()
if (args.rungs % 2) != 0:
print("Rungs must be a multiple of 2")
return
print("int ladder(int *foo, int *bar, int x) {")
rung1 = range(0, args.rungs, 2)
rung2 = range(1, args.rungs, 2)
for i in rung1:
print("rung1%d:" % i)
print("*foo = x++;")
if i != rung1[-1]:
print("if (*bar) goto rung1%d;" % (i + 2))
print("else goto rung2%d;" % (i + 1))
else:
print("goto rung2%d;" % (i + 1))
for i in rung2:
print("rung2%d:" % i)
print("*foo = x++;")
if i != rung2[-1]:
print("goto rung2%d;" % (i + 2))
else:
print("return *foo;")
print("}")
if __name__ == "__main__":
main()

View File

@ -18,21 +18,23 @@ import multiprocessing
args = None
def parse_line(line):
question = line.find('?')
question = line.find("?")
if question == -1:
return None, None
open_paren = line.find('(', question)
open_paren = line.find("(", question)
if open_paren == -1:
return None, None
close_paren = line.rfind(')', open_paren)
close_paren = line.rfind(")", open_paren)
if open_paren == -1:
return None, None
mangled = line[question : open_paren]
demangled = line[open_paren+1 : close_paren]
mangled = line[question:open_paren]
demangled = line[open_paren + 1 : close_paren]
return mangled.strip(), demangled.strip()
class Result(object):
def __init__(self):
self.crashed = []
@ -41,6 +43,7 @@ class Result(object):
self.errors = set()
self.nfiles = 0
class MapContext(object):
def __init__(self):
self.rincomplete = None
@ -48,18 +51,19 @@ class MapContext(object):
self.pending_objs = []
self.npending = 0
def process_file(path, objdump):
r = Result()
r.file = path
popen_args = [objdump, '-t', '-demangle', path]
popen_args = [objdump, "-t", "-demangle", path]
p = subprocess.Popen(popen_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = p.communicate()
if p.returncode != 0:
r.crashed = [r.file]
return r
output = stdout.decode('utf-8')
output = stdout.decode("utf-8")
for line in output.splitlines():
mangled, demangled = parse_line(line)
@ -70,15 +74,25 @@ def process_file(path, objdump):
r.errors.add(mangled)
return r
def add_results(r1, r2):
r1.crashed.extend(r2.crashed)
r1.errors.update(r2.errors)
r1.nsymbols += r2.nsymbols
r1.nfiles += r2.nfiles
def print_result_row(directory, result):
print("[{0} files, {1} crashes, {2} errors, {3} symbols]: '{4}'".format(
result.nfiles, len(result.crashed), len(result.errors), result.nsymbols, directory))
print(
"[{0} files, {1} crashes, {2} errors, {3} symbols]: '{4}'".format(
result.nfiles,
len(result.crashed),
len(result.errors),
result.nsymbols,
directory,
)
)
def process_one_chunk(pool, chunk_size, objdump, context):
objs = []
@ -112,7 +126,7 @@ def process_one_chunk(pool, chunk_size, objdump, context):
re.nfiles += ntaken
assert(len(objs) == chunk_size or context.npending == 0)
assert len(objs) == chunk_size or context.npending == 0
copier = functools.partial(process_file, objdump=objdump)
mapped_results = list(pool.map(copier, objs))
@ -134,17 +148,18 @@ def process_one_chunk(pool, chunk_size, objdump, context):
add_results(context.rcumulative, re)
print_result_row(c, re)
def process_pending_files(pool, chunk_size, objdump, context):
while context.npending >= chunk_size:
process_one_chunk(pool, chunk_size, objdump, context)
def go():
global args
obj_dir = args.dir
extensions = args.extensions.split(',')
extensions = [x if x[0] == '.' else '.' + x for x in extensions]
extensions = args.extensions.split(",")
extensions = [x if x[0] == "." else "." + x for x in extensions]
pool_size = 48
pool = Pool(processes=pool_size)
@ -178,7 +193,7 @@ def go():
# `pool_size` tasks remaining.
process_pending_files(pool, pool_size, args.objdump, context)
assert(context.npending < pool_size);
assert context.npending < pool_size
process_one_chunk(pool, pool_size, args.objdump, context)
total = context.rcumulative
@ -186,43 +201,58 @@ def go():
nsuccess = total.nsymbols - nfailed
ncrashed = len(total.crashed)
if (nfailed > 0):
if nfailed > 0:
print("Failures:")
for m in sorted(total.errors):
print(" " + m)
if (ncrashed > 0):
if ncrashed > 0:
print("Crashes:")
for f in sorted(total.crashed):
print(" " + f)
print("Summary:")
spct = float(nsuccess)/float(total.nsymbols)
fpct = float(nfailed)/float(total.nsymbols)
cpct = float(ncrashed)/float(nfiles)
spct = float(nsuccess) / float(total.nsymbols)
fpct = float(nfailed) / float(total.nsymbols)
cpct = float(ncrashed) / float(nfiles)
print("Processed {0} object files.".format(nfiles))
print("{0}/{1} symbols successfully demangled ({2:.4%})".format(nsuccess, total.nsymbols, spct))
print(
"{0}/{1} symbols successfully demangled ({2:.4%})".format(
nsuccess, total.nsymbols, spct
)
)
print("{0} symbols could not be demangled ({1:.4%})".format(nfailed, fpct))
print("{0} files crashed while demangling ({1:.4%})".format(ncrashed, cpct))
except:
traceback.print_exc()
pool.close()
pool.join()
if __name__ == "__main__":
def_obj = 'obj' if sys.platform == 'win32' else 'o'
parser = argparse.ArgumentParser(description='Demangle all symbols in a tree of object files, looking for failures.')
parser.add_argument('dir', type=str, help='the root directory at which to start crawling')
parser.add_argument('--objdump', type=str, help='path to llvm-objdump. If not specified ' +
'the tool is located as if by `which llvm-objdump`.')
parser.add_argument('--extensions', type=str, default=def_obj,
help='comma separated list of extensions to demangle (e.g. `o,obj`). ' +
'By default this will be `obj` on Windows and `o` otherwise.')
if __name__ == "__main__":
def_obj = "obj" if sys.platform == "win32" else "o"
parser = argparse.ArgumentParser(
description="Demangle all symbols in a tree of object files, looking for failures."
)
parser.add_argument(
"dir", type=str, help="the root directory at which to start crawling"
)
parser.add_argument(
"--objdump",
type=str,
help="path to llvm-objdump. If not specified "
+ "the tool is located as if by `which llvm-objdump`.",
)
parser.add_argument(
"--extensions",
type=str,
default=def_obj,
help="comma separated list of extensions to demangle (e.g. `o,obj`). "
+ "By default this will be `obj` on Windows and `o` otherwise.",
)
args = parser.parse_args()
multiprocessing.freeze_support()
go()

View File

@ -16,183 +16,186 @@ SVN_DATES_REGEX = re.compile(r"\$(Date|LastChangedDate)[^\$]+\$")
def main():
parser = ArgumentParser()
parser.add_argument(
"-v", "--verbose", action="store_true", help="enable debug logging")
parser.add_argument(
"-c",
"--check",
metavar="reference_file",
help="read checksums from reference_file and " +
"check they match checksums of llvm_path.")
parser.add_argument(
"--partial",
action="store_true",
help="ignore projects from reference_file " +
"that are not checked out in llvm_path.")
parser.add_argument(
"--multi_dir",
action="store_true",
help="indicates llvm_path contains llvm, checked out " +
"into multiple directories, as opposed to a " +
"typical single source tree checkout.")
parser.add_argument("llvm_path")
parser = ArgumentParser()
parser.add_argument(
"-v", "--verbose", action="store_true", help="enable debug logging"
)
parser.add_argument(
"-c",
"--check",
metavar="reference_file",
help="read checksums from reference_file and "
+ "check they match checksums of llvm_path.",
)
parser.add_argument(
"--partial",
action="store_true",
help="ignore projects from reference_file "
+ "that are not checked out in llvm_path.",
)
parser.add_argument(
"--multi_dir",
action="store_true",
help="indicates llvm_path contains llvm, checked out "
+ "into multiple directories, as opposed to a "
+ "typical single source tree checkout.",
)
parser.add_argument("llvm_path")
args = parser.parse_args()
if args.check is not None:
with open(args.check, "r") as f:
reference_checksums = ReadLLVMChecksums(f)
else:
reference_checksums = None
args = parser.parse_args()
if args.check is not None:
with open(args.check, "r") as f:
reference_checksums = ReadLLVMChecksums(f)
else:
reference_checksums = None
if args.verbose:
logging.basicConfig(level=logging.DEBUG)
if args.verbose:
logging.basicConfig(level=logging.DEBUG)
llvm_projects = CreateLLVMProjects(not args.multi_dir)
checksums = ComputeLLVMChecksums(args.llvm_path, llvm_projects)
llvm_projects = CreateLLVMProjects(not args.multi_dir)
checksums = ComputeLLVMChecksums(args.llvm_path, llvm_projects)
if reference_checksums is None:
WriteLLVMChecksums(checksums, sys.stdout)
sys.exit(0)
if reference_checksums is None:
WriteLLVMChecksums(checksums, sys.stdout)
sys.exit(0)
if not ValidateChecksums(reference_checksums, checksums, args.partial):
sys.stdout.write("Checksums differ.\nNew checksums:\n")
WriteLLVMChecksums(checksums, sys.stdout)
sys.stdout.write("Reference checksums:\n")
WriteLLVMChecksums(reference_checksums, sys.stdout)
sys.exit(1)
else:
sys.stdout.write("Checksums match.")
if not ValidateChecksums(reference_checksums, checksums, args.partial):
sys.stdout.write("Checksums differ.\nNew checksums:\n")
WriteLLVMChecksums(checksums, sys.stdout)
sys.stdout.write("Reference checksums:\n")
WriteLLVMChecksums(reference_checksums, sys.stdout)
sys.exit(1)
else:
sys.stdout.write("Checksums match.")
def ComputeLLVMChecksums(root_path, projects):
"""Compute checksums for LLVM sources checked out using svn.
"""Compute checksums for LLVM sources checked out using svn.
Args:
root_path: a directory of llvm checkout.
projects: a list of LLVMProject instances, which describe checkout paths,
relative to root_path.
Args:
root_path: a directory of llvm checkout.
projects: a list of LLVMProject instances, which describe checkout paths,
relative to root_path.
Returns:
A dict mapping from project name to project checksum.
"""
hash_algo = hashlib.sha256
Returns:
A dict mapping from project name to project checksum.
"""
hash_algo = hashlib.sha256
def collapse_svn_substitutions(contents):
# Replace svn substitutions for $Date$ and $LastChangedDate$.
# Unfortunately, these are locale-specific.
return SVN_DATES_REGEX.sub("$\1$", contents)
def collapse_svn_substitutions(contents):
# Replace svn substitutions for $Date$ and $LastChangedDate$.
# Unfortunately, these are locale-specific.
return SVN_DATES_REGEX.sub("$\1$", contents)
def read_and_collapse_svn_subsitutions(file_path):
with open(file_path, "rb") as f:
contents = f.read()
new_contents = collapse_svn_substitutions(contents)
if contents != new_contents:
logging.debug("Replaced svn keyword substitutions in %s", file_path)
logging.debug("\n\tBefore\n%s\n\tAfter\n%s", contents, new_contents)
return new_contents
def read_and_collapse_svn_subsitutions(file_path):
with open(file_path, "rb") as f:
contents = f.read()
new_contents = collapse_svn_substitutions(contents)
if contents != new_contents:
logging.debug("Replaced svn keyword substitutions in %s", file_path)
logging.debug("\n\tBefore\n%s\n\tAfter\n%s", contents, new_contents)
return new_contents
project_checksums = dict()
# Hash each project.
for proj in projects:
project_root = os.path.join(root_path, proj.relpath)
if not os.path.exists(project_root):
logging.info("Folder %s doesn't exist, skipping project %s", proj.relpath,
proj.name)
continue
project_checksums = dict()
# Hash each project.
for proj in projects:
project_root = os.path.join(root_path, proj.relpath)
if not os.path.exists(project_root):
logging.info(
"Folder %s doesn't exist, skipping project %s", proj.relpath, proj.name
)
continue
files = list()
files = list()
def add_file_hash(file_path):
if os.path.islink(file_path) and not os.path.exists(file_path):
content = os.readlink(file_path)
else:
content = read_and_collapse_svn_subsitutions(file_path)
hasher = hash_algo()
hasher.update(content)
file_digest = hasher.hexdigest()
logging.debug("Checksum %s for file %s", file_digest, file_path)
files.append((file_path, file_digest))
def add_file_hash(file_path):
if os.path.islink(file_path) and not os.path.exists(file_path):
content = os.readlink(file_path)
else:
content = read_and_collapse_svn_subsitutions(file_path)
hasher = hash_algo()
hasher.update(content)
file_digest = hasher.hexdigest()
logging.debug("Checksum %s for file %s", file_digest, file_path)
files.append((file_path, file_digest))
logging.info("Computing checksum for %s", proj.name)
WalkProjectFiles(root_path, projects, proj, add_file_hash)
logging.info("Computing checksum for %s", proj.name)
WalkProjectFiles(root_path, projects, proj, add_file_hash)
# Compute final checksum.
files.sort(key=lambda x: x[0])
hasher = hash_algo()
for file_path, file_digest in files:
file_path = os.path.relpath(file_path, project_root)
hasher.update(file_path)
hasher.update(file_digest)
project_checksums[proj.name] = hasher.hexdigest()
return project_checksums
# Compute final checksum.
files.sort(key=lambda x: x[0])
hasher = hash_algo()
for file_path, file_digest in files:
file_path = os.path.relpath(file_path, project_root)
hasher.update(file_path)
hasher.update(file_digest)
project_checksums[proj.name] = hasher.hexdigest()
return project_checksums
def WriteLLVMChecksums(checksums, f):
"""Writes checksums to a text file.
"""Writes checksums to a text file.
Args:
checksums: a dict mapping from project name to project checksum (result of
ComputeLLVMChecksums).
f: a file object to write into.
"""
Args:
checksums: a dict mapping from project name to project checksum (result of
ComputeLLVMChecksums).
f: a file object to write into.
"""
for proj in sorted(checksums.keys()):
f.write("{} {}\n".format(checksums[proj], proj))
for proj in sorted(checksums.keys()):
f.write("{} {}\n".format(checksums[proj], proj))
def ReadLLVMChecksums(f):
"""Reads checksums from a text file, produced by WriteLLVMChecksums.
"""Reads checksums from a text file, produced by WriteLLVMChecksums.
Returns:
A dict, mapping from project name to project checksum.
"""
checksums = {}
while True:
line = f.readline()
if line == "":
break
checksum, proj = line.split()
checksums[proj] = checksum
return checksums
Returns:
A dict, mapping from project name to project checksum.
"""
checksums = {}
while True:
line = f.readline()
if line == "":
break
checksum, proj = line.split()
checksums[proj] = checksum
return checksums
def ValidateChecksums(reference_checksums,
new_checksums,
allow_missing_projects=False):
"""Validates that reference_checksums and new_checksums match.
def ValidateChecksums(reference_checksums, new_checksums, allow_missing_projects=False):
"""Validates that reference_checksums and new_checksums match.
Args:
reference_checksums: a dict of reference checksums, mapping from a project
name to a project checksum.
new_checksums: a dict of checksums to be checked, mapping from a project
name to a project checksum.
allow_missing_projects:
When True, reference_checksums may contain more projects than
new_checksums. Projects missing from new_checksums are ignored.
When False, new_checksums and reference_checksums must contain checksums
for the same set of projects. If there is a project in
reference_checksums, missing from new_checksums, ValidateChecksums
will return False.
Args:
reference_checksums: a dict of reference checksums, mapping from a project
name to a project checksum.
new_checksums: a dict of checksums to be checked, mapping from a project
name to a project checksum.
allow_missing_projects:
When True, reference_checksums may contain more projects than
new_checksums. Projects missing from new_checksums are ignored.
When False, new_checksums and reference_checksums must contain checksums
for the same set of projects. If there is a project in
reference_checksums, missing from new_checksums, ValidateChecksums
will return False.
Returns:
True, if checksums match with regards to allow_missing_projects flag value.
False, otherwise.
"""
if not allow_missing_projects:
if len(new_checksums) != len(reference_checksums):
return False
Returns:
True, if checksums match with regards to allow_missing_projects flag value.
False, otherwise.
"""
if not allow_missing_projects:
if len(new_checksums) != len(reference_checksums):
return False
for proj, checksum in new_checksums.items():
# We never computed a checksum for this project.
if proj not in reference_checksums:
return False
# Checksum did not match.
if reference_checksums[proj] != checksum:
return False
for proj, checksum in new_checksums.items():
# We never computed a checksum for this project.
if proj not in reference_checksums:
return False
# Checksum did not match.
if reference_checksums[proj] != checksum:
return False
return True
return True
if __name__ == "__main__":
main()
main()

View File

@ -11,85 +11,89 @@ import sys
class LLVMProject(object):
"""An LLVM project with a descriptive name and a relative checkout path.
"""
"""An LLVM project with a descriptive name and a relative checkout path."""
def __init__(self, name, relpath):
self.name = name
self.relpath = relpath
def __init__(self, name, relpath):
self.name = name
self.relpath = relpath
def is_subproject(self, other_project):
""" Check if self is checked out as a subdirectory of other_project.
"""
return self.relpath.startswith(other_project.relpath)
def is_subproject(self, other_project):
"""Check if self is checked out as a subdirectory of other_project."""
return self.relpath.startswith(other_project.relpath)
def WalkProjectFiles(checkout_root, all_projects, project, visitor):
""" Walk over all files inside a project without recursing into subprojects, '.git' and '.svn' subfolders.
"""Walk over all files inside a project without recursing into subprojects, '.git' and '.svn' subfolders.
checkout_root: root of the LLVM checkout.
all_projects: projects in the LLVM checkout.
project: a project to walk the files of. Must be inside all_projects.
visitor: a function called on each visited file.
"""
assert project in all_projects
"""
assert project in all_projects
ignored_paths = set()
for other_project in all_projects:
if other_project != project and other_project.is_subproject(project):
ignored_paths.add(os.path.join(checkout_root, other_project.relpath))
ignored_paths = set()
for other_project in all_projects:
if other_project != project and other_project.is_subproject(project):
ignored_paths.add(os.path.join(checkout_root, other_project.relpath))
def raise_error(err):
raise err
def raise_error(err):
raise err
project_root = os.path.join(checkout_root, project.relpath)
for root, dirs, files in os.walk(project_root, onerror=raise_error):
dirs[:] = [
d for d in dirs
if d != ".svn" and d != ".git" and
os.path.join(root, d) not in ignored_paths
]
for f in files:
visitor(os.path.join(root, f))
project_root = os.path.join(checkout_root, project.relpath)
for root, dirs, files in os.walk(project_root, onerror=raise_error):
dirs[:] = [
d
for d in dirs
if d != ".svn"
and d != ".git"
and os.path.join(root, d) not in ignored_paths
]
for f in files:
visitor(os.path.join(root, f))
def CreateLLVMProjects(single_tree_checkout):
"""Returns a list of LLVMProject instances, describing relative paths of a typical LLVM checkout.
"""Returns a list of LLVMProject instances, describing relative paths of a typical LLVM checkout.
Args:
single_tree_checkout:
When True, relative paths for each project points to a typical single
source tree checkout.
When False, relative paths for each projects points to a separate
directory. However, clang-tools-extra is an exception, its relative path
will always be 'clang/tools/extra'.
"""
# FIXME: cover all of llvm projects.
Args:
single_tree_checkout:
When True, relative paths for each project points to a typical single
source tree checkout.
When False, relative paths for each projects points to a separate
directory. However, clang-tools-extra is an exception, its relative path
will always be 'clang/tools/extra'.
"""
# FIXME: cover all of llvm projects.
# Projects that reside inside 'projects/' in a single source tree checkout.
ORDINARY_PROJECTS = [
"compiler-rt", "dragonegg", "libcxx", "libcxxabi", "libunwind",
"test-suite"
]
# Projects that reside inside 'tools/' in a single source tree checkout.
TOOLS_PROJECTS = ["clang", "lld", "lldb"]
if single_tree_checkout:
projects = [LLVMProject("llvm", "")]
projects += [
LLVMProject(p, os.path.join("projects", p)) for p in ORDINARY_PROJECTS
# Projects that reside inside 'projects/' in a single source tree checkout.
ORDINARY_PROJECTS = [
"compiler-rt",
"dragonegg",
"libcxx",
"libcxxabi",
"libunwind",
"test-suite",
]
projects += [
LLVMProject(p, os.path.join("tools", p)) for p in TOOLS_PROJECTS
]
projects.append(
LLVMProject("clang-tools-extra",
os.path.join("tools", "clang", "tools", "extra")))
else:
projects = [LLVMProject("llvm", "llvm")]
projects += [LLVMProject(p, p) for p in ORDINARY_PROJECTS]
projects += [LLVMProject(p, p) for p in TOOLS_PROJECTS]
projects.append(
LLVMProject("clang-tools-extra", os.path.join("clang", "tools",
"extra")))
return projects
# Projects that reside inside 'tools/' in a single source tree checkout.
TOOLS_PROJECTS = ["clang", "lld", "lldb"]
if single_tree_checkout:
projects = [LLVMProject("llvm", "")]
projects += [
LLVMProject(p, os.path.join("projects", p)) for p in ORDINARY_PROJECTS
]
projects += [LLVMProject(p, os.path.join("tools", p)) for p in TOOLS_PROJECTS]
projects.append(
LLVMProject(
"clang-tools-extra", os.path.join("tools", "clang", "tools", "extra")
)
)
else:
projects = [LLVMProject("llvm", "llvm")]
projects += [LLVMProject(p, p) for p in ORDINARY_PROJECTS]
projects += [LLVMProject(p, p) for p in TOOLS_PROJECTS]
projects.append(
LLVMProject("clang-tools-extra", os.path.join("clang", "tools", "extra"))
)
return projects

View File

@ -1,6 +1,7 @@
#!/usr/bin/env python
from __future__ import print_function
'''
"""
Helper script to print out the raw content of an ELF section.
Example usages:
```
@ -13,87 +14,133 @@ cat foo.o | extract-section.py -h .text
```
This is merely a wrapper around `llvm-readobj` that focuses on the binary
content as well as providing more formatting options.
'''
"""
# Unfortunately reading binary from stdin is not so trivial in Python...
def read_raw_stdin():
import sys
if sys.version_info >= (3, 0):
reading_source = sys.stdin.buffer
else:
# Windows will always read as string so we need some
# special handling
if sys.platform == 'win32':
if sys.platform == "win32":
import os, msvcrt
msvcrt.setformat(sys.stdin.fileno(), os.O_BINARY)
reading_source = sys.stdin
return reading_source.read()
def get_raw_section_dump(readobj_path, section_name, input_file):
import subprocess
cmd = [readobj_path, '--elf-output-style=GNU', '--hex-dump={}'.format(section_name),
input_file]
cmd = [
readobj_path,
"--elf-output-style=GNU",
"--hex-dump={}".format(section_name),
input_file,
]
proc = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
if input_file == '-':
if input_file == "-":
# From stdin
out,_ = proc.communicate(input=read_raw_stdin())
out, _ = proc.communicate(input=read_raw_stdin())
else:
out,_ = proc.communicate()
out, _ = proc.communicate()
return out.decode('utf-8') if type(out) is not str else out
return out.decode("utf-8") if type(out) is not str else out
if __name__ == '__main__':
if __name__ == "__main__":
import argparse
# The default '-h' (--help) will conflict with our '-h' (hex) format
arg_parser = argparse.ArgumentParser(add_help=False)
arg_parser.add_argument('--readobj-path', metavar='<executable path>', type=str,
help='Path to llvm-readobj')
arg_parser.add_argument('--input-file', metavar='<file>', type=str,
help='Input object file, or \'-\' to read from stdin')
arg_parser.add_argument('section', metavar='<name>', type=str,
help='Name of the section to extract')
arg_parser.add_argument(
"--readobj-path",
metavar="<executable path>",
type=str,
help="Path to llvm-readobj",
)
arg_parser.add_argument(
"--input-file",
metavar="<file>",
type=str,
help="Input object file, or '-' to read from stdin",
)
arg_parser.add_argument(
"section", metavar="<name>", type=str, help="Name of the section to extract"
)
# Output format
format_group = arg_parser.add_mutually_exclusive_group()
format_group.add_argument('-b', dest='format', action='store_const', const='bits',
help='Print out in bits')
arg_parser.add_argument('--byte-indicator', action='store_true',
help='Whether to print a \'.\' every 8 bits in bits printing mode')
arg_parser.add_argument('--bits-endian', metavar='<little/big>', type=str,
choices=['little', 'big'],
help='Print out bits in specified endianness (little or big); defaults to big')
format_group.add_argument('-h', dest='format', action='store_const', const='hex',
help='Print out in hexadecimal')
arg_parser.add_argument('--hex-width', metavar='<# of bytes>', type=int,
help='The width (in byte) of every element in hex printing mode')
format_group.add_argument(
"-b",
dest="format",
action="store_const",
const="bits",
help="Print out in bits",
)
arg_parser.add_argument(
"--byte-indicator",
action="store_true",
help="Whether to print a '.' every 8 bits in bits printing mode",
)
arg_parser.add_argument(
"--bits-endian",
metavar="<little/big>",
type=str,
choices=["little", "big"],
help="Print out bits in specified endianness (little or big); defaults to big",
)
format_group.add_argument(
"-h",
dest="format",
action="store_const",
const="hex",
help="Print out in hexadecimal",
)
arg_parser.add_argument(
"--hex-width",
metavar="<# of bytes>",
type=int,
help="The width (in byte) of every element in hex printing mode",
)
arg_parser.add_argument('--help', action='help')
arg_parser.set_defaults(format='bits', tool_path='llvm-readobj', input_file='-',
byte_indicator=False, hex_width=4, bits_endian='big')
arg_parser.add_argument("--help", action="help")
arg_parser.set_defaults(
format="bits",
tool_path="llvm-readobj",
input_file="-",
byte_indicator=False,
hex_width=4,
bits_endian="big",
)
args = arg_parser.parse_args()
raw_section = get_raw_section_dump(args.tool_path, args.section, args.input_file)
results = []
for line in raw_section.splitlines(False):
if line.startswith('Hex dump'):
if line.startswith("Hex dump"):
continue
parts = line.strip().split(' ')[1:]
parts = line.strip().split(" ")[1:]
for part in parts[:4]:
# exclude any non-hex dump string
try:
val = int(part, 16)
if args.format == 'bits':
if args.format == "bits":
# divided into bytes first
offsets = (24, 16, 8, 0)
if args.bits_endian == 'little':
if args.bits_endian == "little":
offsets = (0, 8, 16, 24)
for byte in [(val >> off) & 0xFF for off in offsets]:
for bit in [(byte >> off) & 1 for off in range(7, -1, -1)]:
results.append(str(bit))
if args.byte_indicator:
results.append('.')
elif args.format == 'hex':
results.append(".")
elif args.format == "hex":
assert args.hex_width <= 4 and args.hex_width > 0
width_bits = args.hex_width * 8
offsets = [off for off in range(32 - width_bits, -1, -width_bits)]
@ -103,4 +150,4 @@ if __name__ == '__main__':
results.append(format_str.format(word))
except:
break
print(' '.join(results), end='')
print(" ".join(results), end="")

View File

@ -36,10 +36,14 @@ def nm_get_symbols(tool, lib):
# that llvm-nm do not demangle by default, but the system nm on AIX does
# that, so the behavior may change in the future,
# '-p' do not waste time sorting the symbols.
cmd = [tool,'-P','-g','-Xany','--no-demangle','-p']
process = subprocess.Popen(cmd+[lib], bufsize=1,
stdout=subprocess.PIPE, stdin=subprocess.PIPE,
universal_newlines=True)
cmd = [tool, "-P", "-g", "-Xany", "--no-demangle", "-p"]
process = subprocess.Popen(
cmd + [lib],
bufsize=1,
stdout=subprocess.PIPE,
stdin=subprocess.PIPE,
universal_newlines=True,
)
process.stdin.close()
for line in process.stdout:
# Look for external symbols that are defined in some section
@ -58,26 +62,29 @@ def nm_get_symbols(tool, lib):
yield (match.group(1), False)
process.wait()
# Define a function which determines if the target is 32-bit Windows (as that's
# where calling convention name decoration happens).
def readobj_is_32bit_windows(tool, lib):
output = subprocess.check_output([tool,'--file-header',lib],
universal_newlines=True)
output = subprocess.check_output(
[tool, "--file-header", lib], universal_newlines=True
)
for line in output.splitlines():
match = re.match('Format: (\S+)', line)
match = re.match("Format: (\S+)", line)
if match:
return (match.group(1) == 'COFF-i386')
return match.group(1) == "COFF-i386"
return False
# MSVC mangles names to ?<identifier_mangling>@<type_mangling>. By examining the
# identifier/type mangling we can decide which symbols could possibly be
# required and which we can discard.
def should_keep_microsoft_symbol(symbol, calling_convention_decoration):
# Keep unmangled (i.e. extern "C") names
if not '?' in symbol:
if not "?" in symbol:
if calling_convention_decoration:
# Remove calling convention decoration from names
match = re.match('[_@]([^@]+)', symbol)
match = re.match("[_@]([^@]+)", symbol)
if match:
symbol = match.group(1)
# Discard floating point/SIMD constants.
@ -87,15 +94,15 @@ def should_keep_microsoft_symbol(symbol, calling_convention_decoration):
# Deleting destructors start with ?_G or ?_E and can be discarded because
# link.exe gives you a warning telling you they can't be exported if you
# don't
elif symbol.startswith('??_G') or symbol.startswith('??_E'):
elif symbol.startswith("??_G") or symbol.startswith("??_E"):
return None
# An anonymous namespace is mangled as ?A(maybe hex number)@. Any symbol
# that mentions an anonymous namespace can be discarded, as the anonymous
# namespace doesn't exist outside of that translation unit.
elif re.search('\?A(0x\w+)?@', symbol):
elif re.search("\?A(0x\w+)?@", symbol):
return None
# Skip X86GenMnemonicTables functions, they are not exposed from llvm/include/.
elif re.match('\?is[A-Z0-9]*@X86@llvm', symbol):
elif re.match("\?is[A-Z0-9]*@X86@llvm", symbol):
return None
# Keep mangled llvm:: and clang:: function symbols. How we detect these is a
# bit of a mess and imprecise, but that avoids having to completely demangle
@ -115,23 +122,24 @@ def should_keep_microsoft_symbol(symbol, calling_convention_decoration):
# ::= .+@ (list of types)
# ::= .*Z (list of types, varargs)
# <throw-spec> ::= exceptions are not allowed
elif re.search('(llvm|clang)@@[A-Z][A-Z0-9_]*[A-JQ].+(X|.+@|.*Z)$', symbol):
elif re.search("(llvm|clang)@@[A-Z][A-Z0-9_]*[A-JQ].+(X|.+@|.*Z)$", symbol):
return symbol
return None
# Itanium manglings are of the form _Z<identifier_mangling><type_mangling>. We
# demangle the identifier mangling to identify symbols that can be safely
# discarded.
def should_keep_itanium_symbol(symbol, calling_convention_decoration):
# Start by removing any calling convention decoration (which we expect to
# see on all symbols, even mangled C++ symbols)
if calling_convention_decoration and symbol.startswith('_'):
if calling_convention_decoration and symbol.startswith("_"):
symbol = symbol[1:]
# Keep unmangled names
if not symbol.startswith('_') and not symbol.startswith('.'):
if not symbol.startswith("_") and not symbol.startswith("."):
return symbol
# Discard manglings that aren't nested names
match = re.match('_Z(T[VTIS])?(N.+)', symbol)
match = re.match("_Z(T[VTIS])?(N.+)", symbol)
if not match:
return None
# Demangle the name. If the name is too complex then we don't need to keep
@ -143,89 +151,93 @@ def should_keep_itanium_symbol(symbol, calling_convention_decoration):
if not names:
return symbol
# Keep llvm:: and clang:: names
elif names[0][0] == '4llvm' or names[0][0] == '5clang':
elif names[0][0] == "4llvm" or names[0][0] == "5clang":
return symbol
# Discard everything else
else:
return None
# Certain kinds of complex manglings we assume cannot be part of a public
# interface, and we handle them by raising an exception.
class TooComplexName(Exception):
pass
# Parse an itanium mangled name from the start of a string and return a
# (name, rest of string) pair.
def parse_itanium_name(arg):
# Check for a normal name
match = re.match('(\d+)(.+)', arg)
match = re.match("(\d+)(.+)", arg)
if match:
n = int(match.group(1))
name = match.group(1)+match.group(2)[:n]
name = match.group(1) + match.group(2)[:n]
rest = match.group(2)[n:]
return name, rest
# Check for constructor/destructor names
match = re.match('([CD][123])(.+)', arg)
match = re.match("([CD][123])(.+)", arg)
if match:
return match.group(1), match.group(2)
# Assume that a sequence of characters that doesn't end a nesting is an
# operator (this is very imprecise, but appears to be good enough)
match = re.match('([^E]+)(.+)', arg)
match = re.match("([^E]+)(.+)", arg)
if match:
return match.group(1), match.group(2)
# Anything else: we can't handle it
return None, arg
# Parse an itanium mangled template argument list from the start of a string
# and throw it away, returning the rest of the string.
def skip_itanium_template(arg):
# A template argument list starts with I
assert arg.startswith('I'), arg
assert arg.startswith("I"), arg
tmp = arg[1:]
while tmp:
# Check for names
match = re.match('(\d+)(.+)', tmp)
match = re.match("(\d+)(.+)", tmp)
if match:
n = int(match.group(1))
tmp = match.group(2)[n:]
tmp = match.group(2)[n:]
continue
# Check for substitutions
match = re.match('S[A-Z0-9]*_(.+)', tmp)
match = re.match("S[A-Z0-9]*_(.+)", tmp)
if match:
tmp = match.group(1)
# Start of a template
elif tmp.startswith('I'):
elif tmp.startswith("I"):
tmp = skip_itanium_template(tmp)
# Start of a nested name
elif tmp.startswith('N'):
elif tmp.startswith("N"):
_, tmp = parse_itanium_nested_name(tmp)
# Start of an expression: assume that it's too complicated
elif tmp.startswith('L') or tmp.startswith('X'):
elif tmp.startswith("L") or tmp.startswith("X"):
raise TooComplexName
# End of the template
elif tmp.startswith('E'):
elif tmp.startswith("E"):
return tmp[1:]
# Something else: probably a type, skip it
else:
tmp = tmp[1:]
return None
# Parse an itanium mangled nested name and transform it into a list of pairs of
# (name, is_template), returning (list, rest of string).
def parse_itanium_nested_name(arg):
# A nested name starts with N
assert arg.startswith('N'), arg
assert arg.startswith("N"), arg
ret = []
# Skip past the N, and possibly a substitution
match = re.match('NS[A-Z0-9]*_(.+)', arg)
match = re.match("NS[A-Z0-9]*_(.+)", arg)
if match:
tmp = match.group(1)
else:
tmp = arg[1:]
# Skip past CV-qualifiers and ref qualifiers
match = re.match('[rVKRO]*(.+)', tmp);
match = re.match("[rVKRO]*(.+)", tmp)
if match:
tmp = match.group(1)
@ -233,7 +245,7 @@ def parse_itanium_nested_name(arg):
# nested name
while tmp:
# An E ends the nested name
if tmp.startswith('E'):
if tmp.startswith("E"):
return ret, tmp[1:]
# Parse a name
name_part, tmp = parse_itanium_name(tmp)
@ -243,7 +255,7 @@ def parse_itanium_nested_name(arg):
is_template = False
# If this name is a template record that, then skip the template
# arguments
if tmp.startswith('I'):
if tmp.startswith("I"):
tmp = skip_itanium_template(tmp)
is_template = True
# Add the name to the list
@ -252,33 +264,34 @@ def parse_itanium_nested_name(arg):
# If we get here then something went wrong
return None, None
# Parse a microsoft mangled symbol and return a list of pairs of
# (name, is_template). This is very rudimentary and does just enough
# in order to determine if the first or second component is a template.
def parse_microsoft_mangling(arg):
# If the name doesn't start with ? this isn't a mangled name
if not arg.startswith('?'):
if not arg.startswith("?"):
return [(arg, False)]
arg = arg[1:]
components = []
while len(arg) > 0:
# If we see an empty component we've reached the end
if arg.startswith('@'):
if arg.startswith("@"):
return components
# Check for a simple name
match = re.match('(\w+)@(.+)', arg)
match = re.match("(\w+)@(.+)", arg)
if match:
components.append((match.group(1), False))
arg = match.group(2)
continue
# Check for a special function name
match = re.match('(\?_?\w)(.+)', arg)
match = re.match("(\?_?\w)(.+)", arg)
if match:
components.append((match.group(1), False))
arg = match.group(2)
continue
# Check for a template name
match = re.match('\?\$(\w+)@[^@]+@(.+)', arg)
match = re.match("\?\$(\w+)@[^@]+@(.+)", arg)
if match:
components.append((match.group(1), True))
arg = match.group(2)
@ -288,6 +301,7 @@ def parse_microsoft_mangling(arg):
return components
return components
def extract_symbols(arg):
llvm_nm_path, should_keep_symbol, calling_convention_decoration, lib = arg
symbol_defs = dict()
@ -296,18 +310,19 @@ def extract_symbols(arg):
symbol = should_keep_symbol(symbol, calling_convention_decoration)
if symbol:
if is_def:
symbol_defs[symbol] = 1 + symbol_defs.setdefault(symbol,0)
symbol_defs[symbol] = 1 + symbol_defs.setdefault(symbol, 0)
else:
symbol_refs.add(symbol)
return (symbol_defs, symbol_refs)
def get_template_name(sym, mangling):
# Parse the mangling into a list of (name, is_template)
try:
if mangling == 'microsoft':
if mangling == "microsoft":
names = parse_microsoft_mangling(sym)
else:
match = re.match('_Z(T[VTIS])?(N.+)', sym)
match = re.match("_Z(T[VTIS])?(N.+)", sym)
if match:
names, _ = parse_itanium_nested_name(match.group(2))
else:
@ -326,41 +341,62 @@ def get_template_name(sym, mangling):
# Not a template
return None
def parse_tool_path(parser, tool, val):
try:
# Close std streams as we don't want any output and we don't
# want the process to wait for something on stdin.
p = subprocess.Popen([val], stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
stdin=subprocess.PIPE,
universal_newlines=True)
p = subprocess.Popen(
[val],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
stdin=subprocess.PIPE,
universal_newlines=True,
)
p.stdout.close()
p.stderr.close()
p.stdin.close()
p.wait()
return val
except Exception:
parser.error(f'Invalid path for {tool}')
parser.error(f"Invalid path for {tool}")
if __name__ == '__main__':
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description='Extract symbols to export from libraries')
parser.add_argument('--mangling', choices=['itanium','microsoft'],
required=True, help='expected symbol mangling scheme')
parser.add_argument('--nm', metavar='path',
type=lambda x: parse_tool_path(parser, 'nm', x),
help='path to the llvm-nm executable')
parser.add_argument('--readobj', metavar='path',
type=lambda x: parse_tool_path(parser, 'readobj', x),
help='path to the llvm-readobj executable')
parser.add_argument('libs', metavar='lib', type=str, nargs='+',
help='libraries to extract symbols from')
parser.add_argument('-o', metavar='file', type=str, help='output to file')
description="Extract symbols to export from libraries"
)
parser.add_argument(
"--mangling",
choices=["itanium", "microsoft"],
required=True,
help="expected symbol mangling scheme",
)
parser.add_argument(
"--nm",
metavar="path",
type=lambda x: parse_tool_path(parser, "nm", x),
help="path to the llvm-nm executable",
)
parser.add_argument(
"--readobj",
metavar="path",
type=lambda x: parse_tool_path(parser, "readobj", x),
help="path to the llvm-readobj executable",
)
parser.add_argument(
"libs",
metavar="lib",
type=str,
nargs="+",
help="libraries to extract symbols from",
)
parser.add_argument("-o", metavar="file", type=str, help="output to file")
args = parser.parse_args()
# How we determine which symbols to keep and which to discard depends on
# the mangling scheme
if args.mangling == 'microsoft':
if args.mangling == "microsoft":
should_keep_symbol = should_keep_microsoft_symbol
else:
should_keep_symbol = should_keep_itanium_symbol
@ -371,17 +407,17 @@ if __name__ == '__main__':
# When invoked by cmake the arguments are the cmake target names of the
# libraries, so we need to add .lib/.a to the end and maybe lib to the
# start to get the filename. Also allow objects.
suffixes = ['.lib','.a','.obj','.o']
suffixes = [".lib", ".a", ".obj", ".o"]
if not any([lib.endswith(s) for s in suffixes]):
for s in suffixes:
if os.path.exists(lib+s):
lib = lib+s
if os.path.exists(lib + s):
lib = lib + s
break
if os.path.exists('lib'+lib+s):
lib = 'lib'+lib+s
if os.path.exists("lib" + lib + s):
lib = "lib" + lib + s
break
if not any([lib.endswith(s) for s in suffixes]):
print("Don't know what to do with argument "+lib, file=sys.stderr)
print("Don't know what to do with argument " + lib, file=sys.stderr)
exit(1)
libs.append(lib)
@ -398,7 +434,10 @@ if __name__ == '__main__':
# use a lambda or local function definition as that doesn't work on
# windows, so create a list of tuples which duplicates the arguments
# that are the same in all calls.
vals = [(args.nm, should_keep_symbol, calling_convention_decoration, x) for x in libs]
vals = [
(args.nm, should_keep_symbol, calling_convention_decoration, x)
for x in libs
]
# Do an async map then wait for the result to make sure that
# KeyboardInterrupt gets caught correctly (see
# http://bugs.python.org/issue8296)
@ -415,8 +454,8 @@ if __name__ == '__main__':
symbol_defs = dict()
symbol_refs = set()
for (this_lib_defs, this_lib_refs) in libs_symbols:
for k,v in list(this_lib_defs.items()):
symbol_defs[k] = v + symbol_defs.setdefault(k,0)
for k, v in list(this_lib_defs.items()):
symbol_defs[k] = v + symbol_defs.setdefault(k, 0)
for sym in list(this_lib_refs):
symbol_refs.add(sym)
@ -434,10 +473,10 @@ if __name__ == '__main__':
# is because we need to export any explicitly instantiated templates,
# and we expect those to be referenced in some object.
if args.o:
outfile = open(args.o,'w')
outfile = open(args.o, "w")
else:
outfile = sys.stdout
for k,v in list(symbol_defs.items()):
for k, v in list(symbol_defs.items()):
template = get_template_name(k, args.mangling)
if v == 1 and (not template or template in template_instantiation_refs):
print(k, file=outfile)

View File

@ -13,36 +13,38 @@ import shutil
import subprocess
parser = argparse.ArgumentParser()
parser.add_argument('--png', action='store_true')
parser.add_argument("--png", action="store_true")
args = parser.parse_args()
dot = shutil.which('dot')
dot = shutil.which("dot")
if args.png and not dot:
raise RuntimeError("Can't export to PNG without 'dot' in the system")
pattern = re.compile(r"(digraph VPlan {.*?\n})",re.DOTALL)
pattern = re.compile(r"(digraph VPlan {.*?\n})", re.DOTALL)
matches = re.findall(pattern, sys.stdin.read())
for vplan in matches:
m = re.search("graph \[.+(VF=.+,UF.+)", vplan)
if not m:
raise ValueError("Can't get the right VPlan name")
name = re.sub('[^a-zA-Z0-9]', '', m.group(1))
name = re.sub("[^a-zA-Z0-9]", "", m.group(1))
if args.png:
filename = 'VPlan' + name + '.png'
filename = "VPlan" + name + ".png"
print("Exporting " + name + " to PNG via dot: " + filename)
p = subprocess.Popen([dot, '-Tpng', '-o', filename],
encoding='utf-8',
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
p = subprocess.Popen(
[dot, "-Tpng", "-o", filename],
encoding="utf-8",
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
out, err = p.communicate(input=vplan)
if err:
raise RuntimeError("Error running dot: " + err)
else:
filename = 'VPlan' + name + '.dot'
filename = "VPlan" + name + ".dot"
print("Exporting " + name + " to DOT: " + filename)
with open(filename, 'w') as out:
with open(filename, "w") as out:
out.write(vplan)

View File

@ -34,146 +34,158 @@ import sys
from typing import Generator, Sequence, Tuple
_distance_threshold = 3
_prefixes = {'CHECK'}
_suffixes = {'-DAG', '-COUNT', '-EMPTY', '-LABEL', '-NEXT', '-NOT', '-SAME'}
_prefixes = {"CHECK"}
_suffixes = {"-DAG", "-COUNT", "-EMPTY", "-LABEL", "-NEXT", "-NOT", "-SAME"}
# 'NOTE' and 'TODO' are not directives, but are likely to be false positives
# if encountered and to generate noise as a result. We filter them out also to
# avoid this.
_lit_directives = {
'RUN',
'REQUIRES',
'UNSUPPORTED',
'XFAIL',
'DEFINE',
'REDEFINE',
"RUN",
"REQUIRES",
"UNSUPPORTED",
"XFAIL",
"DEFINE",
"REDEFINE",
}
# 'COM' and 'RUN' are default comment prefixes for FileCheck.
_comment_prefixes = {'COM', 'RUN'}
_ignore = _lit_directives.union(_comment_prefixes).union({'NOTE', 'TODO'})
_comment_prefixes = {"COM", "RUN"}
_ignore = _lit_directives.union(_comment_prefixes).union({"NOTE", "TODO"})
def levenshtein(s1: str, s2: str) -> int: # pylint: disable=g-doc-args
"""Computes the edit distance between two strings.
"""Computes the edit distance between two strings.
Additions, deletions, and substitutions all count as a single operation.
"""
if not s1:
return len(s2)
if not s2:
return len(s1)
Additions, deletions, and substitutions all count as a single operation.
"""
if not s1:
return len(s2)
if not s2:
return len(s1)
distances = range(len(s2) + 1)
for i in range(len(s1)):
new_distances = [i + 1]
for j in range(len(s2)):
cost = min(distances[j] + int(s1[i] != s2[j]), distances[j + 1] + 1,
new_distances[-1] + 1)
new_distances.append(cost)
distances = new_distances
return distances[-1]
distances = range(len(s2) + 1)
for i in range(len(s1)):
new_distances = [i + 1]
for j in range(len(s2)):
cost = min(
distances[j] + int(s1[i] != s2[j]),
distances[j + 1] + 1,
new_distances[-1] + 1,
)
new_distances.append(cost)
distances = new_distances
return distances[-1]
class FileRange:
"""Stores the coordinates of a span on a single line within a file.
"""Stores the coordinates of a span on a single line within a file.
Attributes:
line: the line number
start_column: the (inclusive) column where the span starts
end_column: the (inclusive) column where the span ends
"""
line: int
start_column: int
end_column: int
def __init__(self, content: str, start_byte: int, end_byte: int): # pylint: disable=g-doc-args
"""Derives a span's coordinates based on a string and start/end bytes.
`start_byte` and `end_byte` are assumed to be on the same line.
Attributes:
line: the line number
start_column: the (inclusive) column where the span starts
end_column: the (inclusive) column where the span ends
"""
content_before_span = content[:start_byte]
self.line = content_before_span.count('\n') + 1
self.start_column = start_byte - content_before_span.rfind('\n')
self.end_column = self.start_column + (end_byte - start_byte - 1)
def __str__(self) -> str:
return f'{self.line}:{self.start_column}-{self.end_column}'
line: int
start_column: int
end_column: int
def __init__(
self, content: str, start_byte: int, end_byte: int
): # pylint: disable=g-doc-args
"""Derives a span's coordinates based on a string and start/end bytes.
`start_byte` and `end_byte` are assumed to be on the same line.
"""
content_before_span = content[:start_byte]
self.line = content_before_span.count("\n") + 1
self.start_column = start_byte - content_before_span.rfind("\n")
self.end_column = self.start_column + (end_byte - start_byte - 1)
def __str__(self) -> str:
return f"{self.line}:{self.start_column}-{self.end_column}"
class Diagnostic:
"""Stores information about one typo and a suggested fix.
"""Stores information about one typo and a suggested fix.
Attributes:
filepath: the path to the file in which the typo was found
filerange: the position at which the typo was found in the file
typo: the typo
fix: a suggested fix
"""
Attributes:
filepath: the path to the file in which the typo was found
filerange: the position at which the typo was found in the file
typo: the typo
fix: a suggested fix
"""
filepath: pathlib.Path
filerange: FileRange
typo: str
fix: str
filepath: pathlib.Path
filerange: FileRange
typo: str
fix: str
def __init__(
self,
filepath: pathlib.Path,
filerange: FileRange,
typo: str,
fix: str # pylint: disable=redefined-outer-name
):
self.filepath = filepath
self.filerange = filerange
self.typo = typo
self.fix = fix
def __init__(
self,
filepath: pathlib.Path,
filerange: FileRange,
typo: str,
fix: str, # pylint: disable=redefined-outer-name
):
self.filepath = filepath
self.filerange = filerange
self.typo = typo
self.fix = fix
def __str__(self) -> str:
return f'{self.filepath}:' + str(self.filerange) + f': {self.summary()}'
def __str__(self) -> str:
return f"{self.filepath}:" + str(self.filerange) + f": {self.summary()}"
def summary(self) -> str:
return (
f'Found potentially misspelled directive "{self.typo}". Did you mean '
f'"{self.fix}"?')
def summary(self) -> str:
return (
f'Found potentially misspelled directive "{self.typo}". Did you mean '
f'"{self.fix}"?'
)
def find_potential_directives(
content: str,) -> Generator[Tuple[FileRange, str], None, None]:
"""Extracts all the potential FileCheck directives from a string.
content: str,
) -> Generator[Tuple[FileRange, str], None, None]:
"""Extracts all the potential FileCheck directives from a string.
What constitutes a potential directive is loosely defined---we err on the side
of capturing more strings than is necessary, rather than missing any.
What constitutes a potential directive is loosely defined---we err on the side
of capturing more strings than is necessary, rather than missing any.
Args:
content: the string in which to look for directives
Args:
content: the string in which to look for directives
Yields:
Tuples (p, d) where p is the span where the potential directive occurs
within the string and d is the potential directive.
"""
directive_pattern = re.compile(
r'(?:^|//|;|#)[^\d\w\-_]*([\d\w\-_][\s\d\w\-_]*):', re.MULTILINE)
for match in re.finditer(directive_pattern, content):
potential_directive, span = match.group(1), match.span(1)
yield (FileRange(content, span[0], span[1]), potential_directive)
Yields:
Tuples (p, d) where p is the span where the potential directive occurs
within the string and d is the potential directive.
"""
directive_pattern = re.compile(
r"(?:^|//|;|#)[^\d\w\-_]*([\d\w\-_][\s\d\w\-_]*):", re.MULTILINE
)
for match in re.finditer(directive_pattern, content):
potential_directive, span = match.group(1), match.span(1)
yield (FileRange(content, span[0], span[1]), potential_directive)
# TODO(bchetioui): also parse comment prefixes to ignore.
def parse_custom_prefixes(content: str) -> Generator[str, None, None]: # pylint: disable=g-doc-args
"""Parses custom prefixes defined in the string provided.
def parse_custom_prefixes(
content: str,
) -> Generator[str, None, None]: # pylint: disable=g-doc-args
"""Parses custom prefixes defined in the string provided.
For example, given the following file content:
RUN: something | FileCheck %s -check-prefixes CHECK1,CHECK2
RUN: something_else | FileCheck %s -check-prefix 'CHECK3'
For example, given the following file content:
RUN: something | FileCheck %s -check-prefixes CHECK1,CHECK2
RUN: something_else | FileCheck %s -check-prefix 'CHECK3'
the custom prefixes are CHECK1, CHECK2, and CHECK3.
"""
param_re = r'|'.join([r"'[^']*'", r'"[^"]*"', r'[^\'"\s]+'])
for m in re.finditer(r'-check-prefix(?:es)?(?:\s+|=)({})'.format(param_re),
content):
prefixes = m.group(1)
if prefixes.startswith('\'') or prefixes.startswith('"'):
prefixes = prefixes[1:-1]
for prefix in prefixes.split(','):
yield prefix
the custom prefixes are CHECK1, CHECK2, and CHECK3.
"""
param_re = r"|".join([r"'[^']*'", r'"[^"]*"', r'[^\'"\s]+'])
for m in re.finditer(
r"-check-prefix(?:es)?(?:\s+|=)({})".format(param_re), content
):
prefixes = m.group(1)
if prefixes.startswith("'") or prefixes.startswith('"'):
prefixes = prefixes[1:-1]
for prefix in prefixes.split(","):
yield prefix
def find_directive_typos(
@ -181,71 +193,79 @@ def find_directive_typos(
filepath: pathlib.Path,
threshold: int = 3,
) -> Generator[Diagnostic, None, None]:
"""Detects potential typos in FileCheck directives.
"""Detects potential typos in FileCheck directives.
Args:
content: the content of the file
filepath: the path to the file to check for typos in directives
threshold: the (inclusive) maximum edit distance between a potential
directive and an actual directive, such that the potential directive is
classified as a typo
Args:
content: the content of the file
filepath: the path to the file to check for typos in directives
threshold: the (inclusive) maximum edit distance between a potential
directive and an actual directive, such that the potential directive is
classified as a typo
Yields:
Diagnostics, in order from the top of the file.
"""
all_prefixes = _prefixes.union(set(parse_custom_prefixes(content)))
all_directives = ([
f'{prefix}{suffix}'
for prefix, suffix in itertools.product(all_prefixes, _suffixes)
] + list(_ignore) + list(all_prefixes))
def find_best_match(typo):
return min(
[(threshold + 1, typo)] + [(levenshtein(typo, d), d)
for d in all_directives
if abs(len(d) - len(typo)) <= threshold],
key=lambda tup: tup[0],
Yields:
Diagnostics, in order from the top of the file.
"""
all_prefixes = _prefixes.union(set(parse_custom_prefixes(content)))
all_directives = (
[
f"{prefix}{suffix}"
for prefix, suffix in itertools.product(all_prefixes, _suffixes)
]
+ list(_ignore)
+ list(all_prefixes)
)
potential_directives = find_potential_directives(content)
def find_best_match(typo):
return min(
[(threshold + 1, typo)]
+ [
(levenshtein(typo, d), d)
for d in all_directives
if abs(len(d) - len(typo)) <= threshold
],
key=lambda tup: tup[0],
)
for filerange, potential_directive in potential_directives:
# TODO(bchetioui): match count directives more finely. We skip directives
# starting with 'CHECK-COUNT-' for the moment as they require more complex
# logic to be handled correctly.
if any(
potential_directive.startswith(f'{prefix}-COUNT-')
for prefix in all_prefixes):
continue
potential_directives = find_potential_directives(content)
# Ignoring potential typos that will not be matched later due to a too low
# threshold, in order to avoid potentially long computation times.
if len(potential_directive) > max(map(len, all_directives)) + threshold:
continue
for filerange, potential_directive in potential_directives:
# TODO(bchetioui): match count directives more finely. We skip directives
# starting with 'CHECK-COUNT-' for the moment as they require more complex
# logic to be handled correctly.
if any(
potential_directive.startswith(f"{prefix}-COUNT-")
for prefix in all_prefixes
):
continue
score, best_match = find_best_match(potential_directive)
if score == 0: # This is an actual directive, ignore.
continue
elif score <= threshold and best_match not in _ignore:
yield Diagnostic(filepath, filerange, potential_directive, best_match)
# Ignoring potential typos that will not be matched later due to a too low
# threshold, in order to avoid potentially long computation times.
if len(potential_directive) > max(map(len, all_directives)) + threshold:
continue
score, best_match = find_best_match(potential_directive)
if score == 0: # This is an actual directive, ignore.
continue
elif score <= threshold and best_match not in _ignore:
yield Diagnostic(filepath, filerange, potential_directive, best_match)
def main(argv: Sequence[str]):
if len(argv) < 2:
print(f'Usage: {argv[0]} path/to/file/1 ... path/to/file/n')
exit(1)
if len(argv) < 2:
print(f"Usage: {argv[0]} path/to/file/1 ... path/to/file/n")
exit(1)
for filepath in argv[1:]:
logging.info('Checking %s', filepath)
with open(filepath, 'rt') as f:
content = f.read()
for diagnostic in find_directive_typos(
content,
pathlib.Path(filepath),
threshold=_distance_threshold,
):
print(diagnostic)
for filepath in argv[1:]:
logging.info("Checking %s", filepath)
with open(filepath, "rt") as f:
content = f.read()
for diagnostic in find_directive_typos(
content,
pathlib.Path(filepath),
threshold=_distance_threshold,
):
print(diagnostic)
if __name__ == '__main__':
main(sys.argv)
if __name__ == "__main__":
main(sys.argv)

View File

@ -11,68 +11,72 @@ import filecheck_lint as fcl
class TestParser(unittest.TestCase):
def test_parse_all_additional_prefixes(self):
def run(content, expected_prefixes):
prefixes = set(fcl.parse_custom_prefixes(content))
for prefix in expected_prefixes:
self.assertIn(prefix, prefixes)
def test_parse_all_additional_prefixes(self):
def run(content, expected_prefixes):
prefixes = set(fcl.parse_custom_prefixes(content))
for prefix in expected_prefixes:
self.assertIn(prefix, prefixes)
for content, expected_prefixes in [
('-check-prefix=PREFIX', {'PREFIX'}),
('-check-prefix=\'PREFIX\'', {'PREFIX'}),
('-check-prefix="PREFIX"', {'PREFIX'}),
('-check-prefix PREFIX', {'PREFIX'}),
('-check-prefix PREFIX', {'PREFIX'}),
('-check-prefixes=PREFIX1,PREFIX2', {'PREFIX1', 'PREFIX2'}),
('-check-prefixes PREFIX1,PREFIX2', {'PREFIX1', 'PREFIX2'}),
(
"""-check-prefix=PREFIX1 -check-prefix PREFIX2
for content, expected_prefixes in [
("-check-prefix=PREFIX", {"PREFIX"}),
("-check-prefix='PREFIX'", {"PREFIX"}),
('-check-prefix="PREFIX"', {"PREFIX"}),
("-check-prefix PREFIX", {"PREFIX"}),
("-check-prefix PREFIX", {"PREFIX"}),
("-check-prefixes=PREFIX1,PREFIX2", {"PREFIX1", "PREFIX2"}),
("-check-prefixes PREFIX1,PREFIX2", {"PREFIX1", "PREFIX2"}),
(
"""-check-prefix=PREFIX1 -check-prefix PREFIX2
-check-prefixes=PREFIX3,PREFIX4 -check-prefix=PREFIX5
-check-prefixes PREFIX6,PREFIX7 -check-prefixes=PREFIX8',
""", # pylint: disable=bad-continuation
{f'PREFIX{i}' for i in range(1, 9)}),
]:
run(content, expected_prefixes)
{f"PREFIX{i}" for i in range(1, 9)},
),
]:
run(content, expected_prefixes)
def test_additional_prefixes_uniquely(self):
lines = ['--check-prefix=SOME-PREFIX', '--check-prefix=SOME-PREFIX']
prefixes = set(fcl.parse_custom_prefixes('\n'.join(lines)))
assert len(prefixes) == 1
def test_additional_prefixes_uniquely(self):
lines = ["--check-prefix=SOME-PREFIX", "--check-prefix=SOME-PREFIX"]
prefixes = set(fcl.parse_custom_prefixes("\n".join(lines)))
assert len(prefixes) == 1
class TestTypoDetection(unittest.TestCase):
def test_find_potential_directives_comment_prefix(self):
lines = ["junk; CHCK1:", "junk// CHCK2:", "SOME CHCK3:"]
content = "\n".join(lines)
def test_find_potential_directives_comment_prefix(self):
lines = ['junk; CHCK1:', 'junk// CHCK2:', 'SOME CHCK3:']
content = '\n'.join(lines)
results = list(fcl.find_potential_directives(content))
assert len(results) == 3
pos, match = results[0]
assert (
pos.line == 1
and pos.start_column == len("junk; ") + 1
and pos.end_column == len(lines[0]) - 1
)
assert match == "CHCK1"
results = list(fcl.find_potential_directives(content))
assert len(results) == 3
pos, match = results[0]
assert (pos.line == 1 and
pos.start_column == len('junk; ') + 1 and
pos.end_column == len(lines[0]) - 1)
assert match == 'CHCK1'
pos, match = results[1]
assert (
pos.line == 2
and pos.start_column == len("junk// ") + 1
and pos.end_column == len(lines[1]) - 1
)
assert match == "CHCK2"
pos, match = results[1]
assert (pos.line == 2 and
pos.start_column == len('junk// ') + 1 and
pos.end_column == len(lines[1]) - 1)
assert match == 'CHCK2'
pos, match = results[2]
assert (
pos.line == 3
and pos.start_column == 1
and pos.end_column == len(lines[2]) - 1
)
assert match == "SOME CHCK3"
pos, match = results[2]
assert (pos.line == 3 and
pos.start_column == 1 and
pos.end_column == len(lines[2]) - 1)
assert match == 'SOME CHCK3'
def test_levenshtein(self):
for s1, s2, distance in [
('Levenshtein', 'Levenstin', 2), # 2 insertions
('Levenshtein', 'Levenstherin', 3), # 1 insertion, 2 deletions
('Levenshtein', 'Lenvinshtein', 2), # 1 deletion, 1 substitution
('Levenshtein', 'Levenshtein', 0), # identical strings
]:
assert fcl.levenshtein(s1, s2) == distance
def test_levenshtein(self):
for s1, s2, distance in [
("Levenshtein", "Levenstin", 2), # 2 insertions
("Levenshtein", "Levenstherin", 3), # 1 insertion, 2 deletions
("Levenshtein", "Lenvinshtein", 2), # 1 deletion, 1 substitution
("Levenshtein", "Levenshtein", 0), # identical strings
]:
assert fcl.levenshtein(s1, s2) == distance

View File

@ -5,495 +5,550 @@ import sys
import gdb.printing
import gdb.types
class Iterator:
def __iter__(self):
return self
def __iter__(self):
return self
if sys.version_info.major == 2:
def next(self):
return self.__next__()
if sys.version_info.major == 2:
def next(self):
return self.__next__()
def children(self):
return self
def children(self):
return self
class SmallStringPrinter:
"""Print an llvm::SmallString object."""
"""Print an llvm::SmallString object."""
def __init__(self, val):
self.val = val
def __init__(self, val):
self.val = val
def to_string(self):
data = self.val['BeginX'].cast(gdb.lookup_type('char').pointer())
length = self.val['Size']
return data.lazy_string(length=length)
def to_string(self):
data = self.val["BeginX"].cast(gdb.lookup_type("char").pointer())
length = self.val["Size"]
return data.lazy_string(length=length)
def display_hint(self):
return "string"
def display_hint (self):
return 'string'
class StringRefPrinter:
"""Print an llvm::StringRef object."""
"""Print an llvm::StringRef object."""
def __init__(self, val):
self.val = val
def __init__(self, val):
self.val = val
def to_string(self):
data = self.val['Data']
length = self.val['Length']
return data.lazy_string(length=length)
def to_string(self):
data = self.val["Data"]
length = self.val["Length"]
return data.lazy_string(length=length)
def display_hint(self):
return "string"
def display_hint(self):
return 'string'
class SmallVectorPrinter(Iterator):
"""Print an llvm::SmallVector object."""
"""Print an llvm::SmallVector object."""
def __init__(self, val):
self.val = val
t = val.type.template_argument(0).pointer()
self.begin = val['BeginX'].cast(t)
self.size = val['Size']
self.i = 0
def __init__(self, val):
self.val = val
t = val.type.template_argument(0).pointer()
self.begin = val["BeginX"].cast(t)
self.size = val["Size"]
self.i = 0
def __next__(self):
if self.i == self.size:
raise StopIteration
ret = '[{}]'.format(self.i), (self.begin+self.i).dereference()
self.i += 1
return ret
def __next__(self):
if self.i == self.size:
raise StopIteration
ret = "[{}]".format(self.i), (self.begin + self.i).dereference()
self.i += 1
return ret
def to_string(self):
return 'llvm::SmallVector of Size {}, Capacity {}'.format(self.size, self.val['Capacity'])
def to_string(self):
return "llvm::SmallVector of Size {}, Capacity {}".format(
self.size, self.val["Capacity"]
)
def display_hint(self):
return "array"
def display_hint (self):
return 'array'
class ArrayRefPrinter:
"""Print an llvm::ArrayRef object."""
"""Print an llvm::ArrayRef object."""
class _iterator:
def __init__(self, begin, end):
self.cur = begin
self.end = end
self.count = 0
class _iterator:
def __init__(self, begin, end):
self.cur = begin
self.end = end
self.count = 0
def __iter__(self):
return self
def __iter__(self):
return self
def __next__(self):
if self.cur == self.end:
raise StopIteration
count = self.count
self.count = self.count + 1
cur = self.cur
self.cur = self.cur + 1
return '[%d]' % count, cur.dereference()
def __next__(self):
if self.cur == self.end:
raise StopIteration
count = self.count
self.count = self.count + 1
cur = self.cur
self.cur = self.cur + 1
return "[%d]" % count, cur.dereference()
if sys.version_info.major == 2:
next = __next__
if sys.version_info.major == 2:
next = __next__
def __init__(self, val):
self.val = val
def __init__(self, val):
self.val = val
def children(self):
data = self.val['Data']
return self._iterator(data, data + self.val['Length'])
def children(self):
data = self.val["Data"]
return self._iterator(data, data + self.val["Length"])
def to_string(self):
return 'llvm::ArrayRef of length %d' % (self.val['Length'])
def to_string(self):
return "llvm::ArrayRef of length %d" % (self.val["Length"])
def display_hint(self):
return "array"
def display_hint (self):
return 'array'
class ExpectedPrinter(Iterator):
"""Print an llvm::Expected object."""
"""Print an llvm::Expected object."""
def __init__(self, val):
self.val = val
def __next__(self):
val = self.val
if val is None:
raise StopIteration
self.val = None
if val['HasError']:
return ('error', val['ErrorStorage'].address.cast(
gdb.lookup_type('llvm::ErrorInfoBase').pointer()).dereference())
return ('value', val['TStorage'].address.cast(
val.type.template_argument(0).pointer()).dereference())
def to_string(self):
return 'llvm::Expected{}'.format(' is error' if self.val['HasError'] else '')
class OptionalPrinter(Iterator):
"""Print an llvm::Optional object."""
def __init__(self, val):
self.val = val
def __next__(self):
val = self.val
if val is None:
raise StopIteration
self.val = None
if not val['Storage']['hasVal']:
raise StopIteration
return ('value', val['Storage']['val'])
def to_string(self):
return 'llvm::Optional{}'.format('' if self.val['Storage']['hasVal'] else ' is not initialized')
class DenseMapPrinter:
"Print a DenseMap"
class _iterator:
def __init__(self, key_info_t, begin, end):
self.key_info_t = key_info_t
self.cur = begin
self.end = end
self.advancePastEmptyBuckets()
self.first = True
def __iter__(self):
return self
def advancePastEmptyBuckets(self):
# disabled until the comments below can be addressed
# keeping as notes/posterity/hints for future contributors
return
n = self.key_info_t.name
is_equal = gdb.parse_and_eval(n + '::isEqual')
empty = gdb.parse_and_eval(n + '::getEmptyKey()')
tombstone = gdb.parse_and_eval(n + '::getTombstoneKey()')
# the following is invalid, GDB fails with:
# Python Exception <class 'gdb.error'> Attempt to take address of value
# not located in memory.
# because isEqual took parameter (for the unsigned long key I was testing)
# by const ref, and GDB
# It's also not entirely general - we should be accessing the "getFirst()"
# member function, not the 'first' member variable, but I've yet to figure
# out how to find/call member functions (especially (const) overloaded
# ones) on a gdb.Value.
while self.cur != self.end and (is_equal(self.cur.dereference()['first'], empty) or is_equal(self.cur.dereference()['first'], tombstone)):
self.cur = self.cur + 1
def __init__(self, val):
self.val = val
def __next__(self):
if self.cur == self.end:
raise StopIteration
cur = self.cur
v = cur.dereference()['first' if self.first else 'second']
if not self.first:
self.cur = self.cur + 1
self.advancePastEmptyBuckets()
self.first = True
else:
self.first = False
return 'x', v
val = self.val
if val is None:
raise StopIteration
self.val = None
if val["HasError"]:
return (
"error",
val["ErrorStorage"]
.address.cast(gdb.lookup_type("llvm::ErrorInfoBase").pointer())
.dereference(),
)
return (
"value",
val["TStorage"]
.address.cast(val.type.template_argument(0).pointer())
.dereference(),
)
if sys.version_info.major == 2:
next = __next__
def to_string(self):
return "llvm::Expected{}".format(" is error" if self.val["HasError"] else "")
def __init__(self, val):
self.val = val
def children(self):
t = self.val.type.template_argument(3).pointer()
begin = self.val['Buckets'].cast(t)
end = (begin + self.val['NumBuckets']).cast(t)
return self._iterator(self.val.type.template_argument(2), begin, end)
class OptionalPrinter(Iterator):
"""Print an llvm::Optional object."""
def to_string(self):
return 'llvm::DenseMap with %d elements' % (self.val['NumEntries'])
def __init__(self, val):
self.val = val
def __next__(self):
val = self.val
if val is None:
raise StopIteration
self.val = None
if not val["Storage"]["hasVal"]:
raise StopIteration
return ("value", val["Storage"]["val"])
def to_string(self):
return "llvm::Optional{}".format(
"" if self.val["Storage"]["hasVal"] else " is not initialized"
)
class DenseMapPrinter:
"Print a DenseMap"
class _iterator:
def __init__(self, key_info_t, begin, end):
self.key_info_t = key_info_t
self.cur = begin
self.end = end
self.advancePastEmptyBuckets()
self.first = True
def __iter__(self):
return self
def advancePastEmptyBuckets(self):
# disabled until the comments below can be addressed
# keeping as notes/posterity/hints for future contributors
return
n = self.key_info_t.name
is_equal = gdb.parse_and_eval(n + "::isEqual")
empty = gdb.parse_and_eval(n + "::getEmptyKey()")
tombstone = gdb.parse_and_eval(n + "::getTombstoneKey()")
# the following is invalid, GDB fails with:
# Python Exception <class 'gdb.error'> Attempt to take address of value
# not located in memory.
# because isEqual took parameter (for the unsigned long key I was testing)
# by const ref, and GDB
# It's also not entirely general - we should be accessing the "getFirst()"
# member function, not the 'first' member variable, but I've yet to figure
# out how to find/call member functions (especially (const) overloaded
# ones) on a gdb.Value.
while self.cur != self.end and (
is_equal(self.cur.dereference()["first"], empty)
or is_equal(self.cur.dereference()["first"], tombstone)
):
self.cur = self.cur + 1
def __next__(self):
if self.cur == self.end:
raise StopIteration
cur = self.cur
v = cur.dereference()["first" if self.first else "second"]
if not self.first:
self.cur = self.cur + 1
self.advancePastEmptyBuckets()
self.first = True
else:
self.first = False
return "x", v
if sys.version_info.major == 2:
next = __next__
def __init__(self, val):
self.val = val
def children(self):
t = self.val.type.template_argument(3).pointer()
begin = self.val["Buckets"].cast(t)
end = (begin + self.val["NumBuckets"]).cast(t)
return self._iterator(self.val.type.template_argument(2), begin, end)
def to_string(self):
return "llvm::DenseMap with %d elements" % (self.val["NumEntries"])
def display_hint(self):
return "map"
def display_hint(self):
return 'map'
class StringMapPrinter:
"Print a StringMap"
"Print a StringMap"
def __init__(self, val):
self.val = val
def __init__(self, val):
self.val = val
def children(self):
it = self.val['TheTable']
end = (it + self.val['NumBuckets'])
value_ty = self.val.type.template_argument(0)
entry_base_ty = gdb.lookup_type('llvm::StringMapEntryBase')
tombstone = gdb.parse_and_eval('llvm::StringMapImpl::TombstoneIntVal');
def children(self):
it = self.val["TheTable"]
end = it + self.val["NumBuckets"]
value_ty = self.val.type.template_argument(0)
entry_base_ty = gdb.lookup_type("llvm::StringMapEntryBase")
tombstone = gdb.parse_and_eval("llvm::StringMapImpl::TombstoneIntVal")
while it != end:
it_deref = it.dereference()
if it_deref == 0 or it_deref == tombstone:
it = it + 1
continue
while it != end:
it_deref = it.dereference()
if it_deref == 0 or it_deref == tombstone:
it = it + 1
continue
entry_ptr = it_deref.cast(entry_base_ty.pointer())
entry = entry_ptr.dereference()
entry_ptr = it_deref.cast(entry_base_ty.pointer())
entry = entry_ptr.dereference()
str_len = entry['keyLength']
value_ptr = (entry_ptr + 1).cast(value_ty.pointer())
str_data = (entry_ptr + 1).cast(gdb.lookup_type('uintptr_t')) + max(value_ty.sizeof, entry_base_ty.alignof)
str_data = str_data.cast(gdb.lookup_type('char').const().pointer())
string_ref = gdb.Value(struct.pack('PN', int(str_data), int(str_len)), gdb.lookup_type('llvm::StringRef'))
yield 'key', string_ref
str_len = entry["keyLength"]
value_ptr = (entry_ptr + 1).cast(value_ty.pointer())
str_data = (entry_ptr + 1).cast(gdb.lookup_type("uintptr_t")) + max(
value_ty.sizeof, entry_base_ty.alignof
)
str_data = str_data.cast(gdb.lookup_type("char").const().pointer())
string_ref = gdb.Value(
struct.pack("PN", int(str_data), int(str_len)),
gdb.lookup_type("llvm::StringRef"),
)
yield "key", string_ref
value = value_ptr.dereference()
yield 'value', value
value = value_ptr.dereference()
yield "value", value
it = it + 1
it = it + 1
def to_string(self):
return 'llvm::StringMap with %d elements' % (self.val['NumItems'])
def to_string(self):
return "llvm::StringMap with %d elements" % (self.val["NumItems"])
def display_hint(self):
return "map"
def display_hint(self):
return 'map'
class TwinePrinter:
"Print a Twine"
"Print a Twine"
def __init__(self, val):
self._val = val
def __init__(self, val):
self._val = val
def display_hint(self):
return 'string'
def display_hint(self):
return "string"
def string_from_pretty_printer_lookup(self, val):
'''Lookup the default pretty-printer for val and use it.
def string_from_pretty_printer_lookup(self, val):
"""Lookup the default pretty-printer for val and use it.
If no pretty-printer is defined for the type of val, print an error and
return a placeholder string.'''
If no pretty-printer is defined for the type of val, print an error and
return a placeholder string."""
pp = gdb.default_visualizer(val)
if pp:
s = pp.to_string()
pp = gdb.default_visualizer(val)
if pp:
s = pp.to_string()
# The pretty-printer may return a LazyString instead of an actual Python
# string. Convert it to a Python string. However, GDB doesn't seem to
# register the LazyString type, so we can't check
# "type(s) == gdb.LazyString".
if 'LazyString' in type(s).__name__:
s = s.value().string()
# The pretty-printer may return a LazyString instead of an actual Python
# string. Convert it to a Python string. However, GDB doesn't seem to
# register the LazyString type, so we can't check
# "type(s) == gdb.LazyString".
if "LazyString" in type(s).__name__:
s = s.value().string()
else:
print(('No pretty printer for {} found. The resulting Twine ' +
'representation will be incomplete.').format(val.type.name))
s = '(missing {})'.format(val.type.name)
else:
print(
(
"No pretty printer for {} found. The resulting Twine "
+ "representation will be incomplete."
).format(val.type.name)
)
s = "(missing {})".format(val.type.name)
return s
return s
def is_twine_kind(self, kind, expected):
if not kind.endswith(expected):
return False
# apparently some GDB versions add the NodeKind:: namespace
# (happens for me on GDB 7.11)
return kind in ('llvm::Twine::' + expected,
'llvm::Twine::NodeKind::' + expected)
def is_twine_kind(self, kind, expected):
if not kind.endswith(expected):
return False
# apparently some GDB versions add the NodeKind:: namespace
# (happens for me on GDB 7.11)
return kind in (
"llvm::Twine::" + expected,
"llvm::Twine::NodeKind::" + expected,
)
def string_from_child(self, child, kind):
'''Return the string representation of the Twine::Child child.'''
def string_from_child(self, child, kind):
"""Return the string representation of the Twine::Child child."""
if self.is_twine_kind(kind, 'EmptyKind') or self.is_twine_kind(kind, 'NullKind'):
return ''
if self.is_twine_kind(kind, "EmptyKind") or self.is_twine_kind(
kind, "NullKind"
):
return ""
if self.is_twine_kind(kind, 'TwineKind'):
return self.string_from_twine_object(child['twine'].dereference())
if self.is_twine_kind(kind, "TwineKind"):
return self.string_from_twine_object(child["twine"].dereference())
if self.is_twine_kind(kind, 'CStringKind'):
return child['cString'].string()
if self.is_twine_kind(kind, "CStringKind"):
return child["cString"].string()
if self.is_twine_kind(kind, 'StdStringKind'):
val = child['stdString'].dereference()
return self.string_from_pretty_printer_lookup(val)
if self.is_twine_kind(kind, "StdStringKind"):
val = child["stdString"].dereference()
return self.string_from_pretty_printer_lookup(val)
if self.is_twine_kind(kind, 'PtrAndLengthKind'):
val = child['ptrAndLength']
data = val['ptr']
length = val['length']
return data.string(length=length)
if self.is_twine_kind(kind, "PtrAndLengthKind"):
val = child["ptrAndLength"]
data = val["ptr"]
length = val["length"]
return data.string(length=length)
if self.is_twine_kind(kind, 'CharKind'):
return chr(child['character'])
if self.is_twine_kind(kind, "CharKind"):
return chr(child["character"])
if self.is_twine_kind(kind, 'DecUIKind'):
return str(child['decUI'])
if self.is_twine_kind(kind, "DecUIKind"):
return str(child["decUI"])
if self.is_twine_kind(kind, 'DecIKind'):
return str(child['decI'])
if self.is_twine_kind(kind, "DecIKind"):
return str(child["decI"])
if self.is_twine_kind(kind, 'DecULKind'):
return str(child['decUL'].dereference())
if self.is_twine_kind(kind, "DecULKind"):
return str(child["decUL"].dereference())
if self.is_twine_kind(kind, 'DecLKind'):
return str(child['decL'].dereference())
if self.is_twine_kind(kind, "DecLKind"):
return str(child["decL"].dereference())
if self.is_twine_kind(kind, 'DecULLKind'):
return str(child['decULL'].dereference())
if self.is_twine_kind(kind, "DecULLKind"):
return str(child["decULL"].dereference())
if self.is_twine_kind(kind, 'DecLLKind'):
return str(child['decLL'].dereference())
if self.is_twine_kind(kind, "DecLLKind"):
return str(child["decLL"].dereference())
if self.is_twine_kind(kind, 'UHexKind'):
val = child['uHex'].dereference()
return hex(int(val))
if self.is_twine_kind(kind, "UHexKind"):
val = child["uHex"].dereference()
return hex(int(val))
print(('Unhandled NodeKind {} in Twine pretty-printer. The result will be '
'incomplete.').format(kind))
print(
(
"Unhandled NodeKind {} in Twine pretty-printer. The result will be "
"incomplete."
).format(kind)
)
return '(unhandled {})'.format(kind)
return "(unhandled {})".format(kind)
def string_from_twine_object(self, twine):
'''Return the string representation of the Twine object twine.'''
def string_from_twine_object(self, twine):
"""Return the string representation of the Twine object twine."""
lhs = twine['LHS']
rhs = twine['RHS']
lhs = twine["LHS"]
rhs = twine["RHS"]
lhs_kind = str(twine['LHSKind'])
rhs_kind = str(twine['RHSKind'])
lhs_kind = str(twine["LHSKind"])
rhs_kind = str(twine["RHSKind"])
lhs_str = self.string_from_child(lhs, lhs_kind)
rhs_str = self.string_from_child(rhs, rhs_kind)
lhs_str = self.string_from_child(lhs, lhs_kind)
rhs_str = self.string_from_child(rhs, rhs_kind)
return lhs_str + rhs_str
return lhs_str + rhs_str
def to_string(self):
return self.string_from_twine_object(self._val)
def to_string(self):
return self.string_from_twine_object(self._val)
def display_hint(self):
return "string"
def display_hint(self):
return 'string'
def get_pointer_int_pair(val):
"""Get tuple from llvm::PointerIntPair."""
info_name = val.type.template_argument(4).strip_typedefs().name
# Note: this throws a gdb.error if the info type is not used (by means of a
# call to getPointer() or similar) in the current translation unit.
enum_type = gdb.lookup_type(info_name + '::MaskAndShiftConstants')
enum_dict = gdb.types.make_enum_dict(enum_type)
ptr_mask = enum_dict[info_name + '::PointerBitMask']
int_shift = enum_dict[info_name + '::IntShift']
int_mask = enum_dict[info_name + '::IntMask']
pair_union = val['Value']
pointer = (pair_union & ptr_mask)
value = ((pair_union >> int_shift) & int_mask)
return (pointer, value)
"""Get tuple from llvm::PointerIntPair."""
info_name = val.type.template_argument(4).strip_typedefs().name
# Note: this throws a gdb.error if the info type is not used (by means of a
# call to getPointer() or similar) in the current translation unit.
enum_type = gdb.lookup_type(info_name + "::MaskAndShiftConstants")
enum_dict = gdb.types.make_enum_dict(enum_type)
ptr_mask = enum_dict[info_name + "::PointerBitMask"]
int_shift = enum_dict[info_name + "::IntShift"]
int_mask = enum_dict[info_name + "::IntMask"]
pair_union = val["Value"]
pointer = pair_union & ptr_mask
value = (pair_union >> int_shift) & int_mask
return (pointer, value)
class PointerIntPairPrinter:
"""Print a PointerIntPair."""
"""Print a PointerIntPair."""
def __init__(self, pointer, value):
self.pointer = pointer
self.value = value
def __init__(self, pointer, value):
self.pointer = pointer
self.value = value
def children(self):
yield ('pointer', self.pointer)
yield ('value', self.value)
def children(self):
yield ("pointer", self.pointer)
yield ("value", self.value)
def to_string(self):
return "(%s, %s)" % (self.pointer.type, self.value.type)
def to_string(self):
return '(%s, %s)' % (self.pointer.type, self.value.type)
def make_pointer_int_pair_printer(val):
"""Factory for an llvm::PointerIntPair printer."""
try:
pointer, value = get_pointer_int_pair(val)
except gdb.error:
return None # If PointerIntPair cannot be analyzed, print as raw value.
pointer_type = val.type.template_argument(0)
value_type = val.type.template_argument(2)
return PointerIntPairPrinter(pointer.cast(pointer_type),
value.cast(value_type))
"""Factory for an llvm::PointerIntPair printer."""
try:
pointer, value = get_pointer_int_pair(val)
except gdb.error:
return None # If PointerIntPair cannot be analyzed, print as raw value.
pointer_type = val.type.template_argument(0)
value_type = val.type.template_argument(2)
return PointerIntPairPrinter(pointer.cast(pointer_type), value.cast(value_type))
class PointerUnionPrinter:
"""Print a PointerUnion."""
"""Print a PointerUnion."""
def __init__(self, pointer):
self.pointer = pointer
def __init__(self, pointer):
self.pointer = pointer
def children(self):
yield ('pointer', self.pointer)
def children(self):
yield ("pointer", self.pointer)
def to_string(self):
return "Containing %s" % self.pointer.type
def to_string(self):
return "Containing %s" % self.pointer.type
def make_pointer_union_printer(val):
"""Factory for an llvm::PointerUnion printer."""
try:
pointer, value = get_pointer_int_pair(val['Val'])
except gdb.error:
return None # If PointerIntPair cannot be analyzed, print as raw value.
pointer_type = val.type.template_argument(int(value))
return PointerUnionPrinter(pointer.cast(pointer_type))
"""Factory for an llvm::PointerUnion printer."""
try:
pointer, value = get_pointer_int_pair(val["Val"])
except gdb.error:
return None # If PointerIntPair cannot be analyzed, print as raw value.
pointer_type = val.type.template_argument(int(value))
return PointerUnionPrinter(pointer.cast(pointer_type))
class IlistNodePrinter:
"""Print an llvm::ilist_node object."""
"""Print an llvm::ilist_node object."""
def __init__(self, val):
impl_type = val.type.fields()[0].type
base_type = impl_type.fields()[0].type
derived_type = val.type.template_argument(0)
def __init__(self, val):
impl_type = val.type.fields()[0].type
base_type = impl_type.fields()[0].type
derived_type = val.type.template_argument(0)
def get_prev_and_sentinel(base):
# One of Prev and PrevAndSentinel exists. Depending on #defines used to
# compile LLVM, the base_type's template argument is either true of false.
if base_type.template_argument(0):
return get_pointer_int_pair(base['PrevAndSentinel'])
return base['Prev'], None
def get_prev_and_sentinel(base):
# One of Prev and PrevAndSentinel exists. Depending on #defines used to
# compile LLVM, the base_type's template argument is either true of false.
if base_type.template_argument(0):
return get_pointer_int_pair(base["PrevAndSentinel"])
return base["Prev"], None
# Casts a base_type pointer to the appropriate derived type.
def cast_pointer(pointer):
sentinel = get_prev_and_sentinel(pointer.dereference())[1]
pointer = pointer.cast(impl_type.pointer())
if sentinel:
return pointer
return pointer.cast(derived_type.pointer())
# Casts a base_type pointer to the appropriate derived type.
def cast_pointer(pointer):
sentinel = get_prev_and_sentinel(pointer.dereference())[1]
pointer = pointer.cast(impl_type.pointer())
if sentinel:
return pointer
return pointer.cast(derived_type.pointer())
# Repeated cast becaue val.type's base_type is ambiguous when using tags.
base = val.cast(impl_type).cast(base_type)
(prev, sentinel) = get_prev_and_sentinel(base)
prev = prev.cast(base_type.pointer())
self.prev = cast_pointer(prev)
self.next = cast_pointer(val['Next'])
self.sentinel = sentinel
# Repeated cast becaue val.type's base_type is ambiguous when using tags.
base = val.cast(impl_type).cast(base_type)
(prev, sentinel) = get_prev_and_sentinel(base)
prev = prev.cast(base_type.pointer())
self.prev = cast_pointer(prev)
self.next = cast_pointer(val["Next"])
self.sentinel = sentinel
def children(self):
if self.sentinel:
yield "sentinel", "yes"
yield "prev", self.prev
yield "next", self.next
def children(self):
if self.sentinel:
yield 'sentinel', 'yes'
yield 'prev', self.prev
yield 'next', self.next
class IlistPrinter:
"""Print an llvm::simple_ilist or llvm::iplist object."""
"""Print an llvm::simple_ilist or llvm::iplist object."""
def __init__(self, val):
self.node_type = val.type.template_argument(0)
sentinel = val['Sentinel']
# First field is common base type of sentinel and ilist_node.
base_type = sentinel.type.fields()[0].type
self.sentinel = sentinel.address.cast(base_type.pointer())
def __init__(self, val):
self.node_type = val.type.template_argument(0)
sentinel = val["Sentinel"]
# First field is common base type of sentinel and ilist_node.
base_type = sentinel.type.fields()[0].type
self.sentinel = sentinel.address.cast(base_type.pointer())
def _pointers(self):
pointer = self.sentinel
while True:
pointer = pointer['Next'].cast(pointer.type)
if pointer == self.sentinel:
return
yield pointer.cast(self.node_type.pointer())
def _pointers(self):
pointer = self.sentinel
while True:
pointer = pointer["Next"].cast(pointer.type)
if pointer == self.sentinel:
return
yield pointer.cast(self.node_type.pointer())
def children(self):
for k, v in enumerate(self._pointers()):
yield ('[%d]' % k, v.dereference())
def children(self):
for k, v in enumerate(self._pointers()):
yield ("[%d]" % k, v.dereference())
pp = gdb.printing.RegexpCollectionPrettyPrinter("LLVMSupport")
pp.add_printer('llvm::SmallString', '^llvm::SmallString<.*>$', SmallStringPrinter)
pp.add_printer('llvm::StringRef', '^llvm::StringRef$', StringRefPrinter)
pp.add_printer('llvm::SmallVectorImpl', '^llvm::SmallVector(Impl)?<.*>$', SmallVectorPrinter)
pp.add_printer('llvm::ArrayRef', '^llvm::(Mutable)?ArrayRef<.*>$', ArrayRefPrinter)
pp.add_printer('llvm::Expected', '^llvm::Expected<.*>$', ExpectedPrinter)
pp.add_printer('llvm::Optional', '^llvm::Optional<.*>$', OptionalPrinter)
pp.add_printer('llvm::DenseMap', '^llvm::DenseMap<.*>$', DenseMapPrinter)
pp.add_printer('llvm::StringMap', '^llvm::StringMap<.*>$', StringMapPrinter)
pp.add_printer('llvm::Twine', '^llvm::Twine$', TwinePrinter)
pp.add_printer('llvm::PointerIntPair', '^llvm::PointerIntPair<.*>$', make_pointer_int_pair_printer)
pp.add_printer('llvm::PointerUnion', '^llvm::PointerUnion<.*>$', make_pointer_union_printer)
pp.add_printer('llvm::ilist_node', '^llvm::ilist_node<.*>$', IlistNodePrinter)
pp.add_printer('llvm::iplist', '^llvm::iplist<.*>$', IlistPrinter)
pp.add_printer('llvm::simple_ilist', '^llvm::simple_ilist<.*>$', IlistPrinter)
pp.add_printer("llvm::SmallString", "^llvm::SmallString<.*>$", SmallStringPrinter)
pp.add_printer("llvm::StringRef", "^llvm::StringRef$", StringRefPrinter)
pp.add_printer(
"llvm::SmallVectorImpl", "^llvm::SmallVector(Impl)?<.*>$", SmallVectorPrinter
)
pp.add_printer("llvm::ArrayRef", "^llvm::(Mutable)?ArrayRef<.*>$", ArrayRefPrinter)
pp.add_printer("llvm::Expected", "^llvm::Expected<.*>$", ExpectedPrinter)
pp.add_printer("llvm::Optional", "^llvm::Optional<.*>$", OptionalPrinter)
pp.add_printer("llvm::DenseMap", "^llvm::DenseMap<.*>$", DenseMapPrinter)
pp.add_printer("llvm::StringMap", "^llvm::StringMap<.*>$", StringMapPrinter)
pp.add_printer("llvm::Twine", "^llvm::Twine$", TwinePrinter)
pp.add_printer(
"llvm::PointerIntPair", "^llvm::PointerIntPair<.*>$", make_pointer_int_pair_printer
)
pp.add_printer(
"llvm::PointerUnion", "^llvm::PointerUnion<.*>$", make_pointer_union_printer
)
pp.add_printer("llvm::ilist_node", "^llvm::ilist_node<.*>$", IlistNodePrinter)
pp.add_printer("llvm::iplist", "^llvm::iplist<.*>$", IlistPrinter)
pp.add_printer("llvm::simple_ilist", "^llvm::simple_ilist<.*>$", IlistPrinter)
gdb.printing.register_pretty_printer(gdb.current_objfile(), pp)

View File

@ -9,7 +9,7 @@
# ==-------------------------------------------------------------------------==#
import argparse
from git import Repo # type: ignore
from git import Repo # type: ignore
import github
import os
import re
@ -18,8 +18,7 @@ import sys
import time
from typing import List, Optional
beginner_comment = \
"""
beginner_comment = """
Hi!
This issue may be a good introductory issue for people new to working on LLVM. If you would like to work on this issue, your first steps are:
@ -39,54 +38,58 @@ For more instructions on how to submit a patch to LLVM, see our [documentation](
If you have any further questions about this issue, don't hesitate to ask via a comment on this Github issue.
"""
class IssueSubscriber:
class IssueSubscriber:
@property
def team_name(self) -> str:
return self._team_name
def __init__(self, token:str, repo:str, issue_number:int, label_name:str):
def __init__(self, token: str, repo: str, issue_number: int, label_name: str):
self.repo = github.Github(token).get_repo(repo)
self.org = github.Github(token).get_organization(self.repo.organization.login)
self.issue = self.repo.get_issue(issue_number)
self._team_name = 'issue-subscribers-{}'.format(label_name).lower()
self._team_name = "issue-subscribers-{}".format(label_name).lower()
def run(self) -> bool:
for team in self.org.get_teams():
if self.team_name != team.name.lower():
continue
comment = ''
if team.slug == 'issue-subscribers-good-first-issue':
comment = '{}\n'.format(beginner_comment)
comment = ""
if team.slug == "issue-subscribers-good-first-issue":
comment = "{}\n".format(beginner_comment)
comment += '@llvm/{}'.format(team.slug)
comment += "@llvm/{}".format(team.slug)
self.issue.create_comment(comment)
return True
return False
def setup_llvmbot_git(git_dir = '.'):
def setup_llvmbot_git(git_dir="."):
"""
Configure the git repo in `git_dir` with the llvmbot account so
commits are attributed to llvmbot.
"""
repo = Repo(git_dir)
with repo.config_writer() as config:
config.set_value('user', 'name', 'llvmbot')
config.set_value('user', 'email', 'llvmbot@llvm.org')
config.set_value("user", "name", "llvmbot")
config.set_value("user", "email", "llvmbot@llvm.org")
def phab_api_call(phab_token:str, url:str, args:dict) -> dict:
def phab_api_call(phab_token: str, url: str, args: dict) -> dict:
"""
Make an API call to the Phabricator web service and return a dictionary
containing the json response.
"""
data = { "api.token" : phab_token }
data = {"api.token": phab_token}
data.update(args)
response = requests.post(url, data = data)
response = requests.post(url, data=data)
return response.json()
def phab_login_to_github_login(phab_token:str, repo:github.Repository.Repository, phab_login:str) -> Optional[str]:
def phab_login_to_github_login(
phab_token: str, repo: github.Repository.Repository, phab_login: str
) -> Optional[str]:
"""
Tries to translate a Phabricator login to a github login by
finding a commit made in Phabricator's Differential.
@ -99,19 +102,21 @@ def phab_login_to_github_login(phab_token:str, repo:github.Repository.Repository
"""
args = {
"constraints[authors][0]" : phab_login,
"constraints[authors][0]": phab_login,
# PHID for "LLVM Github Monorepo" repository
"constraints[repositories][0]" : "PHID-REPO-f4scjekhnkmh7qilxlcy",
"limit" : 1
"constraints[repositories][0]": "PHID-REPO-f4scjekhnkmh7qilxlcy",
"limit": 1,
}
# API documentation: https://reviews.llvm.org/conduit/method/diffusion.commit.search/
r = phab_api_call(phab_token, "https://reviews.llvm.org/api/diffusion.commit.search", args)
data = r['result']['data']
r = phab_api_call(
phab_token, "https://reviews.llvm.org/api/diffusion.commit.search", args
)
data = r["result"]["data"]
if len(data) == 0:
# Can't find any commits associated with this user
return None
commit_sha = data[0]['fields']['identifier']
commit_sha = data[0]["fields"]["identifier"]
committer = repo.get_commit(commit_sha).committer
if not committer:
# This committer had an email address GitHub could not recognize, so
@ -120,36 +125,39 @@ def phab_login_to_github_login(phab_token:str, repo:github.Repository.Repository
return None
return committer.login
def phab_get_commit_approvers(phab_token:str, commit:github.Commit.Commit) -> list:
args = { "corpus" : commit.commit.message }
def phab_get_commit_approvers(phab_token: str, commit: github.Commit.Commit) -> list:
args = {"corpus": commit.commit.message}
# API documentation: https://reviews.llvm.org/conduit/method/differential.parsecommitmessage/
r = phab_api_call(phab_token, "https://reviews.llvm.org/api/differential.parsecommitmessage", args)
review_id = r['result']['revisionIDFieldInfo']['value']
r = phab_api_call(
phab_token, "https://reviews.llvm.org/api/differential.parsecommitmessage", args
)
review_id = r["result"]["revisionIDFieldInfo"]["value"]
if not review_id:
# No Phabricator revision for this commit
return []
args = {
'constraints[ids][0]' : review_id,
'attachments[reviewers]' : True
}
args = {"constraints[ids][0]": review_id, "attachments[reviewers]": True}
# API documentation: https://reviews.llvm.org/conduit/method/differential.revision.search/
r = phab_api_call(phab_token, "https://reviews.llvm.org/api/differential.revision.search", args)
reviewers = r['result']['data'][0]['attachments']['reviewers']['reviewers']
r = phab_api_call(
phab_token, "https://reviews.llvm.org/api/differential.revision.search", args
)
reviewers = r["result"]["data"][0]["attachments"]["reviewers"]["reviewers"]
accepted = []
for reviewer in reviewers:
if reviewer['status'] != 'accepted':
if reviewer["status"] != "accepted":
continue
phid = reviewer['reviewerPHID']
args = { 'constraints[phids][0]' : phid }
phid = reviewer["reviewerPHID"]
args = {"constraints[phids][0]": phid}
# API documentation: https://reviews.llvm.org/conduit/method/user.search/
r = phab_api_call(phab_token, "https://reviews.llvm.org/api/user.search", args)
accepted.append(r['result']['data'][0]['fields']['username'])
accepted.append(r["result"]["data"][0]["fields"]["username"])
return accepted
class ReleaseWorkflow:
CHERRY_PICK_FAILED_LABEL = 'release:cherry-pick-failed'
CHERRY_PICK_FAILED_LABEL = "release:cherry-pick-failed"
"""
This class implements the sub-commands for the release-workflow command.
@ -161,9 +169,16 @@ class ReleaseWorkflow:
based on the text in stdin.
"""
def __init__(self, token:str, repo:str, issue_number:int,
branch_repo_name:str, branch_repo_token:str,
llvm_project_dir:str, phab_token:str) -> None:
def __init__(
self,
token: str,
repo: str,
issue_number: int,
branch_repo_name: str,
branch_repo_token: str,
llvm_project_dir: str,
phab_token: str,
) -> None:
self._token = token
self._repo_name = repo
self._issue_number = issue_number
@ -213,11 +228,13 @@ class ReleaseWorkflow:
@property
def push_url(self) -> str:
return 'https://{}@github.com/{}'.format(self.branch_repo_token, self.branch_repo_name)
return "https://{}@github.com/{}".format(
self.branch_repo_token, self.branch_repo_name
)
@property
def branch_name(self) -> str:
return 'issue{}'.format(self.issue_number)
return "issue{}".format(self.issue_number)
@property
def release_branch_for_issue(self) -> Optional[str]:
@ -225,7 +242,7 @@ class ReleaseWorkflow:
milestone = issue.milestone
if milestone is None:
return None
m = re.search('branch: (.+)',milestone.description)
m = re.search("branch: (.+)", milestone.description)
if m:
return m.group(1)
return None
@ -234,10 +251,14 @@ class ReleaseWorkflow:
print(self.release_branch_for_issue)
def issue_notify_branch(self) -> None:
self.issue.create_comment('/branch {}/{}'.format(self.branch_repo_name, self.branch_name))
self.issue.create_comment(
"/branch {}/{}".format(self.branch_repo_name, self.branch_name)
)
def issue_notify_pull_request(self, pull:github.PullRequest.PullRequest) -> None:
self.issue.create_comment('/pull-request {}#{}'.format(self.branch_repo_name, pull.number))
def issue_notify_pull_request(self, pull: github.PullRequest.PullRequest) -> None:
self.issue.create_comment(
"/pull-request {}#{}".format(self.branch_repo_name, pull.number)
)
def make_ignore_comment(self, comment: str) -> str:
"""
@ -246,20 +267,28 @@ class ReleaseWorkflow:
:param str comment: The comment to ignore
"""
return "<!--IGNORE-->\n"+comment
return "<!--IGNORE-->\n" + comment
def issue_notify_no_milestone(self, comment:List[str]) -> None:
message = "{}\n\nError: Command failed due to missing milestone.".format(''.join(['>' + line for line in comment]))
def issue_notify_no_milestone(self, comment: List[str]) -> None:
message = "{}\n\nError: Command failed due to missing milestone.".format(
"".join([">" + line for line in comment])
)
self.issue.create_comment(self.make_ignore_comment(message))
@property
def action_url(self) -> str:
if os.getenv('CI'):
return 'https://github.com/{}/actions/runs/{}'.format(os.getenv('GITHUB_REPOSITORY'), os.getenv('GITHUB_RUN_ID'))
if os.getenv("CI"):
return "https://github.com/{}/actions/runs/{}".format(
os.getenv("GITHUB_REPOSITORY"), os.getenv("GITHUB_RUN_ID")
)
return ""
def issue_notify_cherry_pick_failure(self, commit:str) -> github.IssueComment.IssueComment:
message = self.make_ignore_comment("Failed to cherry-pick: {}\n\n".format(commit))
def issue_notify_cherry_pick_failure(
self, commit: str
) -> github.IssueComment.IssueComment:
message = self.make_ignore_comment(
"Failed to cherry-pick: {}\n\n".format(commit)
)
action_url = self.action_url
if action_url:
message += action_url + "\n\n"
@ -269,7 +298,9 @@ class ReleaseWorkflow:
issue.add_to_labels(self.CHERRY_PICK_FAILED_LABEL)
return comment
def issue_notify_pull_request_failure(self, branch:str) -> github.IssueComment.IssueComment:
def issue_notify_pull_request_failure(
self, branch: str
) -> github.IssueComment.IssueComment:
message = "Failed to create pull request for {} ".format(branch)
message += self.action_url
return self.issue.create_comment(message)
@ -278,7 +309,7 @@ class ReleaseWorkflow:
if self.CHERRY_PICK_FAILED_LABEL in [l.name for l in self.issue.labels]:
self.issue.remove_from_labels(self.CHERRY_PICK_FAILED_LABEL)
def pr_request_review(self, pr:github.PullRequest.PullRequest):
def pr_request_review(self, pr: github.PullRequest.PullRequest):
"""
This function will try to find the best reviewers for `commits` and
then add a comment requesting review of the backport and assign the
@ -297,11 +328,12 @@ class ReleaseWorkflow:
reviewers.append(login)
if len(reviewers):
message = "{} What do you think about merging this PR to the release branch?".format(
" ".join(["@" + r for r in reviewers]))
" ".join(["@" + r for r in reviewers])
)
pr.create_issue_comment(message)
pr.add_to_assignees(*reviewers)
def create_branch(self, commits:List[str]) -> bool:
def create_branch(self, commits: List[str]) -> bool:
"""
This function attempts to backport `commits` into the branch associated
with `self.issue_number`.
@ -312,31 +344,33 @@ class ReleaseWorkflow:
:param list commits: List of commits to cherry-pick.
"""
print('cherry-picking', commits)
print("cherry-picking", commits)
branch_name = self.branch_name
local_repo = Repo(self.llvm_project_dir)
local_repo.git.checkout(self.release_branch_for_issue)
for c in commits:
try:
local_repo.git.cherry_pick('-x', c)
local_repo.git.cherry_pick("-x", c)
except Exception as e:
self.issue_notify_cherry_pick_failure(c)
raise e
push_url = self.push_url
print('Pushing to {} {}'.format(push_url, branch_name))
local_repo.git.push(push_url, 'HEAD:{}'.format(branch_name), force=True)
print("Pushing to {} {}".format(push_url, branch_name))
local_repo.git.push(push_url, "HEAD:{}".format(branch_name), force=True)
self.issue_notify_branch()
self.issue_remove_cherry_pick_failed_label()
return True
def check_if_pull_request_exists(self, repo:github.Repository.Repository, head:str) -> bool:
def check_if_pull_request_exists(
self, repo: github.Repository.Repository, head: str
) -> bool:
pulls = repo.get_pulls(head=head)
return pulls.totalCount != 0
def create_pull_request(self, owner:str, repo_name:str, branch:str) -> bool:
def create_pull_request(self, owner: str, repo_name: str, branch: str) -> bool:
"""
reate a pull request in `self.branch_repo_name`. The base branch of the
pull request will be chosen based on the the milestone attached to
@ -347,7 +381,7 @@ class ReleaseWorkflow:
https://docs.github.com/en/get-started/quickstart/github-glossary#compare-branch
"""
repo = github.Github(self.token).get_repo(self.branch_repo_name)
issue_ref = '{}#{}'.format(self.repo_name, self.issue_number)
issue_ref = "{}#{}".format(self.repo_name, self.issue_number)
pull = None
release_branch_for_issue = self.release_branch_for_issue
if release_branch_for_issue is None:
@ -357,13 +391,17 @@ class ReleaseWorkflow:
# If the target repo is not a fork of llvm-project, we need to copy
# the branch into the target repo. GitHub only supports cross-repo pull
# requests on forked repos.
head_branch = f'{owner}-{branch}'
head_branch = f"{owner}-{branch}"
local_repo = Repo(self.llvm_project_dir)
push_done = False
for _ in range(0,5):
for _ in range(0, 5):
try:
local_repo.git.fetch(f'https://github.com/{owner}/{repo_name}', f'{branch}:{branch}')
local_repo.git.push(self.push_url, f'{branch}:{head_branch}', force=True)
local_repo.git.fetch(
f"https://github.com/{owner}/{repo_name}", f"{branch}:{branch}"
)
local_repo.git.push(
self.push_url, f"{branch}:{head_branch}", force=True
)
push_done = True
break
except Exception as e:
@ -379,11 +417,13 @@ class ReleaseWorkflow:
print("PR already exists...")
return True
try:
pull = repo.create_pull(title=f"PR for {issue_ref}",
body='resolves {}'.format(issue_ref),
base=release_branch_for_issue,
head=head,
maintainer_can_modify=False)
pull = repo.create_pull(
title=f"PR for {issue_ref}",
body="resolves {}".format(issue_ref),
base=release_branch_for_issue,
head=head,
maintainer_can_modify=False,
)
try:
if self.phab_token:
@ -404,7 +444,6 @@ class ReleaseWorkflow:
# TODO(tstellar): Do you really want to always return True?
return True
def execute_command(self) -> bool:
"""
This function reads lines from STDIN and executes the first command
@ -420,11 +459,11 @@ class ReleaseWorkflow:
command = m.group(1)
args = m.group(2)
if command == 'cherry-pick':
if command == "cherry-pick":
return self.create_branch(args.split())
if command == 'branch':
m = re.match('([^/]+)/([^/]+)/(.+)', args)
if command == "branch":
m = re.match("([^/]+)/([^/]+)/(.+)", args)
if m:
owner = m.group(1)
repo = m.group(2)
@ -435,45 +474,85 @@ class ReleaseWorkflow:
print(sys.stdin.readlines())
return False
parser = argparse.ArgumentParser()
parser.add_argument('--token', type=str, required=True, help='GitHub authentiation token')
parser.add_argument('--repo', type=str, default=os.getenv('GITHUB_REPOSITORY', 'llvm/llvm-project'),
help='The GitHub repository that we are working with in the form of <owner>/<repo> (e.g. llvm/llvm-project)')
subparsers = parser.add_subparsers(dest='command')
parser.add_argument(
"--token", type=str, required=True, help="GitHub authentiation token"
)
parser.add_argument(
"--repo",
type=str,
default=os.getenv("GITHUB_REPOSITORY", "llvm/llvm-project"),
help="The GitHub repository that we are working with in the form of <owner>/<repo> (e.g. llvm/llvm-project)",
)
subparsers = parser.add_subparsers(dest="command")
issue_subscriber_parser = subparsers.add_parser('issue-subscriber')
issue_subscriber_parser.add_argument('--label-name', type=str, required=True)
issue_subscriber_parser.add_argument('--issue-number', type=int, required=True)
issue_subscriber_parser = subparsers.add_parser("issue-subscriber")
issue_subscriber_parser.add_argument("--label-name", type=str, required=True)
issue_subscriber_parser.add_argument("--issue-number", type=int, required=True)
release_workflow_parser = subparsers.add_parser('release-workflow')
release_workflow_parser.add_argument('--llvm-project-dir', type=str, default='.', help='directory containing the llvm-project checout')
release_workflow_parser.add_argument('--issue-number', type=int, required=True, help='The issue number to update')
release_workflow_parser.add_argument('--phab-token', type=str, help='Phabricator conduit API token. See https://reviews.llvm.org/settings/user/<USER>/page/apitokens/')
release_workflow_parser.add_argument('--branch-repo-token', type=str,
help='GitHub authentication token to use for the repository where new branches will be pushed. Defaults to TOKEN.')
release_workflow_parser.add_argument('--branch-repo', type=str, default='llvm/llvm-project-release-prs',
help='The name of the repo where new branches will be pushed (e.g. llvm/llvm-project)')
release_workflow_parser.add_argument('sub_command', type=str, choices=['print-release-branch', 'auto'],
help='Print to stdout the name of the release branch ISSUE_NUMBER should be backported to')
release_workflow_parser = subparsers.add_parser("release-workflow")
release_workflow_parser.add_argument(
"--llvm-project-dir",
type=str,
default=".",
help="directory containing the llvm-project checout",
)
release_workflow_parser.add_argument(
"--issue-number", type=int, required=True, help="The issue number to update"
)
release_workflow_parser.add_argument(
"--phab-token",
type=str,
help="Phabricator conduit API token. See https://reviews.llvm.org/settings/user/<USER>/page/apitokens/",
)
release_workflow_parser.add_argument(
"--branch-repo-token",
type=str,
help="GitHub authentication token to use for the repository where new branches will be pushed. Defaults to TOKEN.",
)
release_workflow_parser.add_argument(
"--branch-repo",
type=str,
default="llvm/llvm-project-release-prs",
help="The name of the repo where new branches will be pushed (e.g. llvm/llvm-project)",
)
release_workflow_parser.add_argument(
"sub_command",
type=str,
choices=["print-release-branch", "auto"],
help="Print to stdout the name of the release branch ISSUE_NUMBER should be backported to",
)
llvmbot_git_config_parser = subparsers.add_parser('setup-llvmbot-git', help='Set the default user and email for the git repo in LLVM_PROJECT_DIR to llvmbot')
llvmbot_git_config_parser = subparsers.add_parser(
"setup-llvmbot-git",
help="Set the default user and email for the git repo in LLVM_PROJECT_DIR to llvmbot",
)
args = parser.parse_args()
if args.command == 'issue-subscriber':
issue_subscriber = IssueSubscriber(args.token, args.repo, args.issue_number, args.label_name)
if args.command == "issue-subscriber":
issue_subscriber = IssueSubscriber(
args.token, args.repo, args.issue_number, args.label_name
)
issue_subscriber.run()
elif args.command == 'release-workflow':
release_workflow = ReleaseWorkflow(args.token, args.repo, args.issue_number,
args.branch_repo, args.branch_repo_token,
args.llvm_project_dir, args.phab_token)
elif args.command == "release-workflow":
release_workflow = ReleaseWorkflow(
args.token,
args.repo,
args.issue_number,
args.branch_repo,
args.branch_repo_token,
args.llvm_project_dir,
args.phab_token,
)
if not release_workflow.release_branch_for_issue:
release_workflow.issue_notify_no_milestone(sys.stdin.readlines())
sys.exit(1)
if args.sub_command == 'print-release-branch':
if args.sub_command == "print-release-branch":
release_workflow.print_release_branch()
else:
if not release_workflow.execute_command():
sys.exit(1)
elif args.command == 'setup-llvmbot-git':
elif args.command == "setup-llvmbot-git":
setup_llvmbot_git()

View File

@ -37,7 +37,7 @@ from shlex import quote
VERBOSE = False
QUIET = False
dev_null_fd = None
z40 = '0000000000000000000000000000000000000000'
z40 = "0000000000000000000000000000000000000000"
def eprint(*args, **kwargs):
@ -63,29 +63,37 @@ def die(msg):
def ask_confirm(prompt):
while True:
query = input('%s (y/N): ' % (prompt))
if query.lower() not in ['y', 'n', '']:
print('Expect y or n!')
continue
return query.lower() == 'y'
query = input("%s (y/N): " % (prompt))
if query.lower() not in ["y", "n", ""]:
print("Expect y or n!")
continue
return query.lower() == "y"
def get_dev_null():
"""Lazily create a /dev/null fd for use in shell()"""
global dev_null_fd
if dev_null_fd is None:
dev_null_fd = open(os.devnull, 'w')
dev_null_fd = open(os.devnull, "w")
return dev_null_fd
def shell(cmd, strip=True, cwd=None, stdin=None, die_on_failure=True,
ignore_errors=False, text=True, print_raw_stderr=False):
def shell(
cmd,
strip=True,
cwd=None,
stdin=None,
die_on_failure=True,
ignore_errors=False,
text=True,
print_raw_stderr=False,
):
# Escape args when logging for easy repro.
quoted_cmd = [quote(arg) for arg in cmd]
cwd_msg = ''
cwd_msg = ""
if cwd:
cwd_msg = ' in %s' % cwd
log_verbose('Running%s: %s' % (cwd_msg, ' '.join(quoted_cmd)))
cwd_msg = " in %s" % cwd
log_verbose("Running%s: %s" % (cwd_msg, " ".join(quoted_cmd)))
err_pipe = subprocess.PIPE
if ignore_errors:
@ -93,29 +101,34 @@ def shell(cmd, strip=True, cwd=None, stdin=None, die_on_failure=True,
err_pipe = get_dev_null()
start = time.time()
p = subprocess.Popen(cmd, cwd=cwd, stdout=subprocess.PIPE, stderr=err_pipe,
stdin=subprocess.PIPE,
universal_newlines=text)
p = subprocess.Popen(
cmd,
cwd=cwd,
stdout=subprocess.PIPE,
stderr=err_pipe,
stdin=subprocess.PIPE,
universal_newlines=text,
)
stdout, stderr = p.communicate(input=stdin)
elapsed = time.time() - start
log_verbose('Command took %0.1fs' % elapsed)
log_verbose("Command took %0.1fs" % elapsed)
if p.returncode == 0 or ignore_errors:
if stderr and not ignore_errors:
if not print_raw_stderr:
eprint('`%s` printed to stderr:' % ' '.join(quoted_cmd))
eprint("`%s` printed to stderr:" % " ".join(quoted_cmd))
eprint(stderr.rstrip())
if strip:
if text:
stdout = stdout.rstrip('\r\n')
stdout = stdout.rstrip("\r\n")
else:
stdout = stdout.rstrip(b'\r\n')
stdout = stdout.rstrip(b"\r\n")
if VERBOSE:
for l in stdout.splitlines():
log_verbose('STDOUT: %s' % l)
log_verbose("STDOUT: %s" % l)
return stdout
err_msg = '`%s` returned %s' % (' '.join(quoted_cmd), p.returncode)
err_msg = "`%s` returned %s" % (" ".join(quoted_cmd), p.returncode)
eprint(err_msg)
if stderr:
eprint(stderr.rstrip())
@ -125,40 +138,47 @@ def shell(cmd, strip=True, cwd=None, stdin=None, die_on_failure=True,
def git(*cmd, **kwargs):
return shell(['git'] + list(cmd), **kwargs)
return shell(["git"] + list(cmd), **kwargs)
def get_revs_to_push(range):
commits = git('rev-list', range).splitlines()
commits = git("rev-list", range).splitlines()
# Reverse the order so we print the oldest commit first
commits.reverse()
return commits
def handle_push(args, local_ref, local_sha, remote_ref, remote_sha):
'''Check a single push request (which can include multiple revisions)'''
log_verbose('Handle push, reproduce with '
'`echo %s %s %s %s | pre-push.py %s %s'
% (local_ref, local_sha, remote_ref, remote_sha, args.remote,
args.url))
"""Check a single push request (which can include multiple revisions)"""
log_verbose(
"Handle push, reproduce with "
"`echo %s %s %s %s | pre-push.py %s %s"
% (local_ref, local_sha, remote_ref, remote_sha, args.remote, args.url)
)
# Handle request to delete
if local_sha == z40:
if not ask_confirm('Are you sure you want to delete "%s" on remote "%s"?' % (remote_ref, args.url)):
if not ask_confirm(
'Are you sure you want to delete "%s" on remote "%s"?'
% (remote_ref, args.url)
):
die("Aborting")
return
# Push a new branch
if remote_sha == z40:
if not ask_confirm('Are you sure you want to push a new branch/tag "%s" on remote "%s"?' % (remote_ref, args.url)):
die("Aborting")
range=local_sha
return
if not ask_confirm(
'Are you sure you want to push a new branch/tag "%s" on remote "%s"?'
% (remote_ref, args.url)
):
die("Aborting")
range = local_sha
return
else:
# Update to existing branch, examine new commits
range='%s..%s' % (remote_sha, local_sha)
# Check that the remote commit exists, otherwise let git proceed
if "commit" not in git('cat-file','-t', remote_sha, ignore_errors=True):
return
# Update to existing branch, examine new commits
range = "%s..%s" % (remote_sha, local_sha)
# Check that the remote commit exists, otherwise let git proceed
if "commit" not in git("cat-file", "-t", remote_sha, ignore_errors=True):
return
revs = get_revs_to_push(range)
if not revs:
@ -168,51 +188,57 @@ def handle_push(args, local_ref, local_sha, remote_ref, remote_sha):
# Print the revision about to be pushed commits
print('Pushing to "%s" on remote "%s"' % (remote_ref, args.url))
for sha in revs:
print(' - ' + git('show', '--oneline', '--quiet', sha))
print(" - " + git("show", "--oneline", "--quiet", sha))
if len(revs) > 1:
if not ask_confirm('Are you sure you want to push %d commits?' % len(revs)):
die('Aborting')
if not ask_confirm("Are you sure you want to push %d commits?" % len(revs)):
die("Aborting")
for sha in revs:
msg = git('log', '--format=%B', '-n1', sha)
if 'Differential Revision' not in msg:
continue
for line in msg.splitlines():
for tag in ['Summary', 'Reviewers', 'Subscribers', 'Tags']:
if line.startswith(tag + ':'):
eprint('Please remove arcanist tags from the commit message (found "%s" tag in %s)' % (tag, sha[:12]))
if len(revs) == 1:
eprint('Try running: llvm/utils/git/arcfilter.sh')
die('Aborting (force push by adding "--no-verify")')
msg = git("log", "--format=%B", "-n1", sha)
if "Differential Revision" not in msg:
continue
for line in msg.splitlines():
for tag in ["Summary", "Reviewers", "Subscribers", "Tags"]:
if line.startswith(tag + ":"):
eprint(
'Please remove arcanist tags from the commit message (found "%s" tag in %s)'
% (tag, sha[:12])
)
if len(revs) == 1:
eprint("Try running: llvm/utils/git/arcfilter.sh")
die('Aborting (force push by adding "--no-verify")')
return
if __name__ == '__main__':
if not shutil.which('git'):
die('error: cannot find git command')
if __name__ == "__main__":
if not shutil.which("git"):
die("error: cannot find git command")
argv = sys.argv[1:]
p = argparse.ArgumentParser(
prog='pre-push', formatter_class=argparse.RawDescriptionHelpFormatter,
description=__doc__)
prog="pre-push",
formatter_class=argparse.RawDescriptionHelpFormatter,
description=__doc__,
)
verbosity_group = p.add_mutually_exclusive_group()
verbosity_group.add_argument('-q', '--quiet', action='store_true',
help='print less information')
verbosity_group.add_argument('-v', '--verbose', action='store_true',
help='print more information')
verbosity_group.add_argument(
"-q", "--quiet", action="store_true", help="print less information"
)
verbosity_group.add_argument(
"-v", "--verbose", action="store_true", help="print more information"
)
p.add_argument('remote', type=str, help='Name of the remote')
p.add_argument('url', type=str, help='URL for the remote')
p.add_argument("remote", type=str, help="Name of the remote")
p.add_argument("url", type=str, help="URL for the remote")
args = p.parse_args(argv)
VERBOSE = args.verbose
QUIET = args.quiet
lines = sys.stdin.readlines()
sys.stdin = open('/dev/tty', 'r')
sys.stdin = open("/dev/tty", "r")
for line in lines:
local_ref, local_sha, remote_ref, remote_sha = line.split()
handle_push(args, local_ref, local_sha, remote_ref, remote_sha)
local_ref, local_sha, remote_ref, remote_sha = line.split()
handle_push(args, local_ref, local_sha, remote_ref, remote_sha)

View File

@ -5,4 +5,4 @@ import subprocess
import sys
# Prefix with ./ to run built binary, not arbitrary stuff from PATH.
sys.exit(subprocess.call(['./' + sys.argv[1]] + sys.argv[2:]))
sys.exit(subprocess.call(["./" + sys.argv[1]] + sys.argv[2:]))

View File

@ -16,29 +16,30 @@ import sys
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--format', required=True,
choices=('linux','mac','win'))
parser.add_argument('source')
parser.add_argument('output')
parser.add_argument("--format", required=True, choices=("linux", "mac", "win"))
parser.add_argument("source")
parser.add_argument("output")
args = parser.parse_args()
symbols = open(args.source).readlines()
if args.format == 'linux':
output_lines = (['LLVM_0 {\n',
' global:\n',] +
[' %s;\n' % s.rstrip() for s in symbols] +
[' local:\n',
' *;\n',
'};\n'])
elif args.format == 'mac':
output_lines = ['_' + s for s in symbols]
if args.format == "linux":
output_lines = (
[
"LLVM_0 {\n",
" global:\n",
]
+ [" %s;\n" % s.rstrip() for s in symbols]
+ [" local:\n", " *;\n", "};\n"]
)
elif args.format == "mac":
output_lines = ["_" + s for s in symbols]
else:
assert args.format == 'win'
output_lines = ['EXPORTS\n'] + [' ' + s for s in symbols]
assert args.format == "win"
output_lines = ["EXPORTS\n"] + [" " + s for s in symbols]
open(args.output, 'w').writelines(output_lines)
open(args.output, "w").writelines(output_lines)
if __name__ == '__main__':
if __name__ == "__main__":
sys.exit(main())

View File

@ -13,15 +13,16 @@ import sys
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--stamp', required=True,
help='name of a file whose mtime is updated on run')
parser.add_argument('source')
parser.add_argument('output')
parser.add_argument(
"--stamp", required=True, help="name of a file whose mtime is updated on run"
)
parser.add_argument("source")
parser.add_argument("output")
args = parser.parse_args()
# FIXME: This should not check the host platform but the target platform
# (which needs to be passed in as an arg), for cross builds.
if sys.platform != 'win32':
if sys.platform != "win32":
try:
os.makedirs(os.path.dirname(args.output))
except OSError as e:
@ -37,12 +38,13 @@ def main():
raise
else:
import shutil
output = args.output + ".exe"
source = args.source + ".exe"
shutil.copyfile(os.path.join(os.path.dirname(output), source), output)
open(args.stamp, 'w') # Update mtime on stamp file.
open(args.stamp, "w") # Update mtime on stamp file.
if __name__ == '__main__':
if __name__ == "__main__":
sys.exit(main())

View File

@ -25,77 +25,87 @@ def patch_gn_file(gn_file, add, remove):
with open(gn_file) as f:
gn_contents = f.read()
if add:
srcs_tok = 'sources = ['
srcs_tok = "sources = ["
tokloc = gn_contents.find(srcs_tok)
while gn_contents.startswith('sources = []', tokloc):
while gn_contents.startswith("sources = []", tokloc):
tokloc = gn_contents.find(srcs_tok, tokloc + 1)
if tokloc == -1: raise ValueError(gn_file + ': No source list')
if tokloc == -1:
raise ValueError(gn_file + ": No source list")
if gn_contents.find(srcs_tok, tokloc + 1) != -1:
raise ValueError(gn_file + ': Multiple source lists')
if gn_contents.find('# NOSORT', 0, tokloc) != -1:
raise ValueError(gn_file + ': Found # NOSORT, needs manual merge')
raise ValueError(gn_file + ": Multiple source lists")
if gn_contents.find("# NOSORT", 0, tokloc) != -1:
raise ValueError(gn_file + ": Found # NOSORT, needs manual merge")
tokloc += len(srcs_tok)
for a in add:
gn_contents = (gn_contents[:tokloc] + ('"%s",' % a) +
gn_contents[tokloc:])
gn_contents = gn_contents[:tokloc] + ('"%s",' % a) + gn_contents[tokloc:]
for r in remove:
gn_contents = gn_contents.replace('"%s",' % r, '')
with open(gn_file, 'w') as f:
gn_contents = gn_contents.replace('"%s",' % r, "")
with open(gn_file, "w") as f:
f.write(gn_contents)
# Run `gn format`.
gn = os.path.join(os.path.dirname(__file__), '..', 'gn.py')
subprocess.check_call([sys.executable, gn, 'format', '-q', gn_file])
gn = os.path.join(os.path.dirname(__file__), "..", "gn.py")
subprocess.check_call([sys.executable, gn, "format", "-q", gn_file])
def sync_source_lists(write):
# Use shell=True on Windows in case git is a bat file.
def git(args): subprocess.check_call(['git'] + args, shell=os.name == 'nt')
def git(args):
subprocess.check_call(["git"] + args, shell=os.name == "nt")
def git_out(args):
return subprocess.check_output(['git'] + args, shell=os.name == 'nt',
universal_newlines=True)
gn_files = git_out(['ls-files', '*BUILD.gn']).splitlines()
return subprocess.check_output(
["git"] + args, shell=os.name == "nt", universal_newlines=True
)
gn_files = git_out(["ls-files", "*BUILD.gn"]).splitlines()
# Matches e.g. | "foo.cpp",|, captures |foo| in group 1.
gn_cpp_re = re.compile(r'^\s*"([^$"]+\.(?:cpp|c|h|S))",$', re.MULTILINE)
# Matches e.g. | bar_sources = [ "foo.cpp" ]|, captures |foo| in group 1.
gn_cpp_re2 = re.compile(
r'^\s*(?:.*_)?sources \+?= \[ "([^$"]+\.(?:cpp|c|h|S))" ]$',
re.MULTILINE)
r'^\s*(?:.*_)?sources \+?= \[ "([^$"]+\.(?:cpp|c|h|S))" ]$', re.MULTILINE
)
# Matches e.g. | foo.cpp|, captures |foo| in group 1.
cmake_cpp_re = re.compile(r'^\s*([A-Za-z_0-9./-]+\.(?:cpp|c|h|S))$',
re.MULTILINE)
cmake_cpp_re = re.compile(r"^\s*([A-Za-z_0-9./-]+\.(?:cpp|c|h|S))$", re.MULTILINE)
changes_by_rev = defaultdict(lambda: defaultdict(lambda: defaultdict(list)))
def find_gitrev(touched_line, in_file):
# re.escape() escapes e.g. '-', which works in practice but has
# undefined behavior according to the POSIX extended regex spec.
posix_re_escape = lambda s: re.sub(r'([.[{()\\*+?|^$])', r'\\\1', s)
cmd = ['log', '--format=%h', '-1', '--pickaxe-regex',
# `\<` / `\>` cause issues on Windows (and is a GNU extension).
# `\b` is a GNU extension and stopped working in Apple Git-143
# (Xcode 13.3).
# `[:space:]` is over 10x faster than `^[:alnum:]` and hopefully
# good enough.
r'-S[[:space:]]%s[[:space:]]' % posix_re_escape(touched_line),
in_file]
posix_re_escape = lambda s: re.sub(r"([.[{()\\*+?|^$])", r"\\\1", s)
cmd = [
"log",
"--format=%h",
"-1",
"--pickaxe-regex",
# `\<` / `\>` cause issues on Windows (and is a GNU extension).
# `\b` is a GNU extension and stopped working in Apple Git-143
# (Xcode 13.3).
# `[:space:]` is over 10x faster than `^[:alnum:]` and hopefully
# good enough.
r"-S[[:space:]]%s[[:space:]]" % posix_re_escape(touched_line),
in_file,
]
return git_out(cmd).rstrip()
# Collect changes to gn files, grouped by revision.
for gn_file in gn_files:
# The CMakeLists.txt for llvm/utils/gn/secondary/foo/BUILD.gn is
# at foo/CMakeLists.txt.
strip_prefix = 'llvm/utils/gn/secondary/'
strip_prefix = "llvm/utils/gn/secondary/"
if not gn_file.startswith(strip_prefix):
continue
cmake_file = os.path.join(
os.path.dirname(gn_file[len(strip_prefix):]), 'CMakeLists.txt')
os.path.dirname(gn_file[len(strip_prefix) :]), "CMakeLists.txt"
)
if not os.path.exists(cmake_file):
continue
def get_sources(source_re, text):
return set([m.group(1) for m in source_re.finditer(text)])
gn_cpp = get_sources(gn_cpp_re, open(gn_file).read())
gn_cpp |= get_sources(gn_cpp_re2, open(gn_file).read())
cmake_cpp = get_sources(cmake_cpp_re, open(cmake_file).read())
@ -107,28 +117,28 @@ def sync_source_lists(write):
for f in files:
rev = find_gitrev(f, cmake_file)
changes_by_rev[rev][gn_file][key].append(f)
by_rev(sorted(cmake_cpp - gn_cpp), 'add')
by_rev(sorted(gn_cpp - cmake_cpp), 'remove')
by_rev(sorted(cmake_cpp - gn_cpp), "add")
by_rev(sorted(gn_cpp - cmake_cpp), "remove")
# Output necessary changes grouped by revision.
for rev in sorted(changes_by_rev):
print('[gn build] Port {0} -- https://reviews.llvm.org/rG{0}'
.format(rev))
print("[gn build] Port {0} -- https://reviews.llvm.org/rG{0}".format(rev))
for gn_file, data in sorted(changes_by_rev[rev].items()):
add = data.get('add', [])
remove = data.get('remove', [])
add = data.get("add", [])
remove = data.get("remove", [])
if write:
patch_gn_file(gn_file, add, remove)
git(['add', gn_file])
git(["add", gn_file])
else:
print(' ' + gn_file)
print(" " + gn_file)
if add:
print(' add:\n' + '\n'.join(' "%s",' % a for a in add))
print(" add:\n" + "\n".join(' "%s",' % a for a in add))
if remove:
print(' remove:\n ' + '\n '.join(remove))
print(" remove:\n " + "\n ".join(remove))
print()
if write:
git(['commit', '-m', '[gn build] Port %s' % rev])
git(["commit", "-m", "[gn build] Port %s" % rev])
else:
print()
@ -137,31 +147,33 @@ def sync_source_lists(write):
def sync_unittests():
# Matches e.g. |add_llvm_unittest_with_input_files|.
unittest_re = re.compile(r'^add_\S+_unittest', re.MULTILINE)
unittest_re = re.compile(r"^add_\S+_unittest", re.MULTILINE)
checked = [ 'bolt', 'clang', 'clang-tools-extra', 'lld', 'llvm' ]
checked = ["bolt", "clang", "clang-tools-extra", "lld", "llvm"]
changed = False
for c in checked:
for root, _, _ in os.walk(os.path.join(c, 'unittests')):
cmake_file = os.path.join(root, 'CMakeLists.txt')
for root, _, _ in os.walk(os.path.join(c, "unittests")):
cmake_file = os.path.join(root, "CMakeLists.txt")
if not os.path.exists(cmake_file):
continue
if not unittest_re.search(open(cmake_file).read()):
continue # Skip CMake files that just add subdirectories.
gn_file = os.path.join('llvm/utils/gn/secondary', root, 'BUILD.gn')
gn_file = os.path.join("llvm/utils/gn/secondary", root, "BUILD.gn")
if not os.path.exists(gn_file):
changed = True
print('missing GN file %s for unittest CMake file %s' %
(gn_file, cmake_file))
print(
"missing GN file %s for unittest CMake file %s"
% (gn_file, cmake_file)
)
return changed
def main():
src = sync_source_lists(len(sys.argv) > 1 and sys.argv[1] == '--write')
src = sync_source_lists(len(sys.argv) > 1 and sys.argv[1] == "--write")
tests = sync_unittests()
if src or tests:
sys.exit(1)
if __name__ == '__main__':
if __name__ == "__main__":
main()

Some files were not shown because too many files have changed in this diff Show More