Support 32 bit and 64 bit decoding with one binary

It is possible to configure the build process such that decoding of 32
bit and 64 bit instructions can be chosen at runtime using an additional
parameter of the decode function. The header file is now entirely
architecture-independent and no longer required any previous defines.

Decoding x86-64 still requires a 64-bit pointer size.
This commit is contained in:
Alexis Engelke
2019-01-13 11:58:59 +01:00
parent 83ea2f0769
commit ed53b4a54d
9 changed files with 138 additions and 78 deletions

View File

@@ -24,24 +24,40 @@ parse_nibble(const char nibble)
int
main(int argc, char** argv)
{
if (argc != 2 && argc != 3)
if (argc != 3 && argc != 4)
{
printf("usage: %s [instruction bytes] ([repetitions])\n", argv[0]);
printf("usage: %s [mode] [instruction bytes] ([repetitions])\n", argv[0]);
return -1;
}
DecodeMode mode;
size_t mode_input = strtoul(argv[1], NULL, 0);
if (mode_input == 32)
{
mode = DECODE_32;
}
else if (mode_input == 64)
{
mode = DECODE_64;
}
else
{
printf("Unknown decode mode\n");
return 1;
}
// Avoid allocation by transforming hex to binary in-place.
uint8_t* code = (uint8_t*) argv[1];
uint8_t* code = (uint8_t*) argv[2];
uint8_t* code_end = code;
char* hex = argv[1];
char* hex = argv[2];
for (; *hex; hex += 2, code_end++)
*code_end = (parse_nibble(hex[0]) << 4) | parse_nibble(hex[1]);
size_t length = (size_t) (code_end - code);
size_t repetitions = 1;
if (argc >= 3)
repetitions = strtoul(argv[2], NULL, 0);
if (argc >= 4)
repetitions = strtoul(argv[3], NULL, 0);
struct timespec time_start;
struct timespec time_end;
@@ -56,7 +72,7 @@ main(int argc, char** argv)
while (current_off != length)
{
size_t remaining = length - current_off;
int retval = decode(code + current_off, remaining, &instr);
int retval = decode(code + current_off, remaining, mode, &instr);
if (retval < 0)
goto fail;
current_off += retval;

View File

@@ -2,7 +2,13 @@
test_driver = executable('test_driver', 'driver.c',
dependencies: libx86decode,
c_args: ['-D_GNU_SOURCE'])
test_args = [files('test.py'), test_driver, archmode]
test_args = [files('test.py'), test_driver]
if decode_32
test_args += ['--32']
endif
if decode_64
test_args += ['--64']
endif
## Test cases

View File

@@ -5,12 +5,12 @@ import statistics
import subprocess
import sys
def run(args, code, expected):
def run(args, mode, code, expected):
inner_reps = 10000000 if args.benchmark else 1
outer_reps = 3 if args.benchmark else 1
times = []
for _ in range(outer_reps):
output = subprocess.check_output([args.driver, code, str(inner_reps)],
output = subprocess.check_output([args.driver, str(mode), code, str(inner_reps)],
universal_newlines=True)
instr, time = tuple(output.split("\n", 1))
if instr != expected:
@@ -22,26 +22,29 @@ def run(args, code, expected):
if args.benchmark:
mean = statistics.mean(times)
stdev = statistics.stdev(times)
print("{:53} {:6.3f} ns (std: {:6.3f} ns)".format(expected, mean, stdev))
print("{:2} {:50} {:6.3f} ns (std: {:6.3f} ns)".format(mode, expected, mean, stdev))
return times
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--benchmark", action="store_true")
parser.add_argument("--32", dest="test_modes", action="append_const", const=32)
parser.add_argument("--64", dest="test_modes", action="append_const", const=64)
parser.add_argument("driver")
parser.add_argument("archmode", choices=[32, 64], type=int)
parser.add_argument("cases", nargs="+", type=argparse.FileType('r'))
args = parser.parse_args()
failed, total = 0, 0
total_times = []
test_modes = frozenset(args.test_modes if args.test_modes else [32, 64])
for file in args.cases:
cases = [tuple(ln.strip().split(maxsplit=2)) for ln in file.readlines()]
for op, code, expected in cases:
if op == "decode32" and args.archmode != 32: continue
if op == "decode64" and args.archmode != 64: continue
case_modes = {"decode":{32,64},"decode32":{32},"decode64":{64}}[op]
if not case_modes & test_modes: continue
# Compatibility with old test system
if expected[0] == '"' and expected[-1] == '"':
@@ -49,7 +52,8 @@ if __name__ == "__main__":
try:
total += 1
total_times += run(args, code, expected)
for mode in case_modes & test_modes:
total_times += run(args, mode, code, expected)
except Exception as e:
failed += 1
print("FAILED: %s" % e)