#!/usr/bin/env python
# Auto-generates an exhaustive and repetitive test for correct bundle-locked
# alignment on x86.
# For every possible offset in an aligned bundle, a bundle-locked group of every
# size in the inclusive range [1, bundle_size] is inserted. An appropriate CHECK
# is added to verify that NOP padding occurred (or did not occur) as expected.
# Run with --align-to-end to generate a similar test with align_to_end for each
# .bundle_lock directive.
# This script runs with Python 2.7 and 3.2+
from __future__ import print_function
import argparse
BUNDLE_SIZE_POW2 = 4
BUNDLE_SIZE = 2**BUNDLE_SIZE_POW2
PREAMBLE = """
# RUN: llvm-mc -filetype=obj -triple i386-pc-linux-gnu %s -o - \\
# RUN: | llvm-objdump -triple i386 -disassemble -no-show-raw-insn - | FileCheck %s
# !!! This test is auto-generated from utils/testgen/mc-bundling-x86-gen.py !!!
# It tests that bundle-aligned grouping works correctly in MC. Read the
# source of the script for more details.
.text
.bundle_align_mode {0}
""".format(
BUNDLE_SIZE_POW2
).lstrip()
ALIGNTO = " .align {0}, 0x90"
NOPFILL = " .fill {0}, 1, 0x90"
def print_bundle_locked_sequence(len, align_to_end=False):
print(" .bundle_lock{0}".format(" align_to_end" if align_to_end else ""))
print(" .rept {0}".format(len))
print(" inc %eax")
print(" .endr")
print(" .bundle_unlock")
def generate(align_to_end=False):
print(PREAMBLE)
ntest = 0
for instlen in range(1, BUNDLE_SIZE + 1):
for offset in range(0, BUNDLE_SIZE):
# Spread out all the instructions to not worry about cross-bundle
# interference.
print(ALIGNTO.format(2 * BUNDLE_SIZE))
print("INSTRLEN_{0}_OFFSET_{1}:".format(instlen, offset))
if offset > 0:
print(NOPFILL.format(offset))
print_bundle_locked_sequence(instlen, align_to_end)
# Now generate an appropriate CHECK line
base_offset = ntest * 2 * BUNDLE_SIZE
inst_orig_offset = base_offset + offset # had it not been padded...
def print_check(adjusted_offset=None, nop_split_offset=None):
if adjusted_offset is not None:
print("# CHECK: {0:x}: nop".format(inst_orig_offset))
if nop_split_offset is not None:
print("# CHECK: {0:x}: nop".format(nop_split_offset))
print("# CHECK: {0:x}: incl".format(adjusted_offset))
else:
print("# CHECK: {0:x}: incl".format(inst_orig_offset))
if align_to_end:
if offset + instlen == BUNDLE_SIZE:
# No padding needed
print_check()
elif offset + instlen < BUNDLE_SIZE:
# Pad to end at nearest bundle boundary
offset_to_end = base_offset + (BUNDLE_SIZE - instlen)
print_check(offset_to_end)
else: # offset + instlen > BUNDLE_SIZE
# Pad to end at next bundle boundary, splitting the nop sequence
# at the nearest bundle boundary
offset_to_nearest_bundle = base_offset + BUNDLE_SIZE
offset_to_end = base_offset + (BUNDLE_SIZE * 2 - instlen)
if offset_to_nearest_bundle == offset_to_end:
offset_to_nearest_bundle = None
print_check(offset_to_end, offset_to_nearest_bundle)
else:
if offset + instlen > BUNDLE_SIZE:
# Padding needed
aligned_offset = (inst_orig_offset + instlen) & ~(BUNDLE_SIZE - 1)
print_check(aligned_offset)
else:
# No padding needed
print_check()
print()
ntest += 1
if __name__ == "__main__":
argparser = argparse.ArgumentParser()
argparser.add_argument(
"--align-to-end",
action="store_true",
help="generate .bundle_lock with align_to_end option",
)
args = argparser.parse_args()
generate(align_to_end=args.align_to_end)