diff --git a/src/etc/platform-intrinsics/generator.py b/src/etc/platform-intrinsics/generator.py new file mode 100644 index 00000000000..28378021efd --- /dev/null +++ b/src/etc/platform-intrinsics/generator.py @@ -0,0 +1,482 @@ +# Copyright 2015 The Rust Project Developers. See the COPYRIGHT +# file at the top-level directory of this distribution and at +# http://rust-lang.org/COPYRIGHT. +# +# Licensed under the Apache License, Version 2.0 or the MIT license +# , at your +# option. This file may not be copied, modified, or distributed +# except according to those terms. + +from __future__ import division, print_function +import json +import argparse +import sys +import re +import textwrap + +SPEC = re.compile( + r'^(?:(?P[iusfIUSF])(?:\((?P\d*)-(?P\d*)\)|(?P\d*))' + + r'|(?P\d+)(?P[vShdnwus]*)(?Px\d+)?)$' +) + +class IntrinsicSet(object): + def __init__(self, json): + self._platform = json['platform'] + self._intrinsic_prefix = json['intrinsic_prefix'] + self._llvm_prefix = json['llvm_prefix'] + self._intrinsics = json['intrinsics'] + self._type_info = json['number_info'] + self._widths = json['widths'] + + def intrinsics(self): + for raw in self._intrinsics: + yield GenericIntrinsic(self, + raw['intrinsic'], raw['width'], raw['llvm'], + raw['ret'], raw['args']) + def llvm_prefix(self): + return self._llvm_prefix + def intrinsic_prefix(self): + return self._intrinsic_prefix + + def width_name(self, bitwidth): + return self._widths[str(bitwidth)] + + def number_type_info(self, value): + data = self._type_info[value.__class__.__name__.lower()] + bitwidth = value.bitwidth() + raw_dtype = data['data_type'] + try: + dtype = raw_dtype[str(bitwidth)] + except KeyError: + dtype = raw_dtype['pattern'].format(bitwidth = bitwidth) + return PlatformTypeInfo(data['kind'], + dtype, + value.llvm_name()) + +class PlatformTypeInfo(object): + def __init__(self, kind, data_type, llvm_name, width = None): + self.kind = kind + self.data_type = data_type + self.llvm_name = llvm_name + if width is not None: + self.width = width + def vectorize(self, length, width): + return PlatformTypeInfo(self.kind, self.data_type, + 'v{}{}'.format(length, self.llvm_name), + width) + +class Type(object): + def __init__(self, bitwidth): + self._bitwidth = bitwidth + + def bitwidth(self): + return self._bitwidth + def modify(self, spec, width): + raise NotImplementedError() + +class Number(Type): + def __init__(self, bitwidth): + Type.__init__(self, bitwidth) + def modify(self, spec, width): + if spec == 'w': + return self.__class__(self.bitwidth() * 2) + elif spec == 'n': + return self.__class__(self.bitwidth() // 2) + elif spec == 'v': + return Vector(self, width // self.bitwidth()) + else: + raise ValueError('unknown modification spec {}', spec) + + def type_info(self, platform_info): + return platform_info.number_type_info(self) + +class Signed(Number): + def __init__(self, bitwidth): + Number.__init__(self, bitwidth) + def modify(self, spec, width): + if spec == 'u': + return Unsigned(self.bitwidth()) + elif spec == 's': + return Signed(self.bitwidth()) + else: + return Number.modify(self, spec, width) + def compiler_ctor(self): + return 'i({})'.format(self.bitwidth()) + def llvm_name(self): + return 'i{}'.format(self.bitwidth()) + def rust_name(self): + return 'i{}'.format(self.bitwidth()) + +class Unsigned(Number): + def __init__(self, bitwidth): + Number.__init__(self, bitwidth) + def modify(self, spec, width): + if spec == 'u': + return Unsigned(self.bitwidth()) + elif spec == 's': + return Signed(self.bitwidth()) + else: + return Number.modify(self, spec, width) + def compiler_ctor(self): + return 'u({})'.format(self.bitwidth()) + def llvm_name(self): + return 'i{}'.format(self.bitwidth()) + def rust_name(self): + return 'u{}'.format(self.bitwidth()) +class Float(Number): + def __init__(self, bitwidth): + assert bitwidth in (32, 64) + Number.__init__(self, bitwidth) + def compiler_ctor(self): + return 'f({})'.format(self.bitwidth()) + def llvm_name(self): + return 'f{}'.format(self.bitwidth()) + def rust_name(self): + return 'f{}'.format(self.bitwidth()) + +class Vector(Type): + def __init__(self, elem, length): + assert isinstance(elem, Type) and not isinstance(elem, Vector) + Type.__init__(self, + elem.bitwidth() * length) + self._length = length + self._elem = elem + def modify(self, spec, width): + if spec == 'h': + return Vector(self._elem, self._length // 2) + elif spec == 'd': + return Vector(self._elem, self._length * 2) + elif spec.startswith('x'): + new_bitwidth = int(spec[1:]) + return Vector(self._elem, new_bitwidth // self._elem.bitwidth()) + else: + return Vector(self._elem.modify(spec, width), self._length) + def compiler_ctor(self): + return 'v({}, {})'.format(self._elem.compiler_ctor(), self._length) + def rust_name(self): + return '{}x{}'.format(self._elem.rust_name(), self._length) + def type_info(self, platform_info): + elem_info = self._elem.type_info(platform_info) + return elem_info.vectorize(self._length, + platform_info.width_name(self.bitwidth())) + +class Aggregate(Type): + def __init__(self, flatten, elems): + self._flatten = flatten + self._elems = elems + Type.__init__(self, + sum(elem.bitwidth() for elem in elems)) + def __repr__(self): + return ''.format(self._elems) + def compiler_ctor(self): + return 'agg({}, vec![{}])'.format('true' if self._flatten else 'false', + ', '.join(elem.compiler_ctor() for elem in self._elems)) + def rust_name(self): + return '({})'.format(', '.join(elem.rust_name() for elem in self._elems)) + def type_info(self, platform_info): + #return PlatformTypeInfo(None, None, self._llvm_name) + return None + + +TYPE_ID_LOOKUP = {'i': [Signed, Unsigned], + 's': [Signed], + 'u': [Unsigned], + 'f': [Float]} + +class TypeSpec(object): + def __init__(self, spec): + if not isinstance(spec, list): + spec = [spec] + + self.spec = spec + def enumerate(self, width): + for spec in self.spec: + match = SPEC.match(spec) + if match: + id = match.group('id') + is_vector = id.islower() + type_ctors = TYPE_ID_LOOKUP[id.lower()] + start = match.group('start') + if start is not None: + end = match.group('end') + else: + start = end = match.group('width') + start = int(start) + end = int(end) + + bitwidth = start + while bitwidth <= end: + for ctor in type_ctors: + scalar = ctor(bitwidth) + if is_vector: + + yield Vector(scalar, width // bitwidth) + else: + yield scalar + bitwidth *= 2 + else: + print('Failed to parse: {}'.format(spec), file=sys.stderr) + def resolve(self, width, zero): + assert len(self.spec) == 1 + spec = self.spec[0] + match = SPEC.match(spec) + if match: + id = match.group('id') + if id is not None: + options = list(self.enumerate(width)) + assert len(options) == 1 + return options[0] + reference = match.group('reference') + if reference != '0': + raise NotImplementedError('only argument 0 (return value) references are supported') + ret = zero + for x in match.group('modifiers') or []: + ret = ret.modify(x, width) + force = match.group('force_width') + if force is not None: + ret = ret.modify(force, width) + return ret + elif spec.startswith('('): + if spec.endswith(')'): + raise NotImplementedError() + elif spec.endswith(')f'): + true_spec = spec[1:-2] + flatten = True + elems = [TypeSpec(subspec).resolve(width, zero) for subspec in true_spec.split(',')] + return Aggregate(flatten, elems) + +class GenericIntrinsic(object): + def __init__(self, platform, intrinsic, widths, llvm_name, ret, args): + self._platform = platform + self.intrinsic = intrinsic + self.widths = map(int, widths) + self.llvm_name = llvm_name + self.ret = TypeSpec(ret) + self.args = list(map(TypeSpec, args)) + + def monomorphise(self): + for width in self.widths: + assert width in (64, 128) + for ret in self.ret.enumerate(width): + args = [arg.resolve(width, ret) for arg in self.args] + yield MonomorphicIntrinsic(self._platform, self.intrinsic, width, self.llvm_name, + ret, args) + +class MonomorphicIntrinsic(object): + def __init__(self, platform, intrinsic, width, llvm_name, ret, args): + self._platform = platform + self._intrinsic = intrinsic + self._width = '' if width == 64 else 'q' + self._llvm_name = llvm_name + self._ret_raw = ret + self._ret = ret.type_info(platform) + self._args_raw = args + self._args = [arg.type_info(platform) for arg in args] + def llvm_name(self): + if self._llvm_name.startswith('!'): + return self._llvm_name[1:].format(self._ret, *self._args) + else: + return self._platform.llvm_prefix() + self._llvm_name.format(self._ret, *self._args) + def intrinsic_suffix(self): + return self._intrinsic.format(self._ret, + *self._args, + width = self._width) + def intrinsic_name(self): + return self._platform.intrinsic_prefix() + self.intrinsic_suffix() + def compiler_args(self): + return ', '.join(arg.compiler_ctor() for arg in self._args_raw) + def compiler_ret(self): + return self._ret_raw.compiler_ctor() + def compiler_signature(self): + return '({}) -> {}'.format(self.compiler_args(), self.compiler_ret()) + def intrinsic_signature(self): + names = 'xyzwabcdef' + return '({}) -> {}'.format(', '.join('{}: {}'.format(name, arg.rust_name()) + for name, arg in zip(names, self._args_raw)), + self._ret.rust_name()) + +def parse_args(): + parser = argparse.ArgumentParser( + formatter_class = argparse.RawDescriptionHelpFormatter, + description = 'Render an intrinsic definition JSON to various formats.', + epilog = textwrap.dedent('''\ + An intrinsic definition consists of a map with fields: + - intrinsic: pattern for the name(s) of the vendor's C intrinsic(s) + - llvm: pattern for the name(s) of the internal llvm intrinsic(s) + - width: a vector of vector bit-widths the pattern works with + - ret: type specifier for the return value + - arguments: vector of type specifiers for arguments + + The width and types describe a range of possible intrinsics, + and these are fed back into the intrinsic and llvm patterns to + create the appropriate definitions. + + ## Type specifier grammar + + ``` + type := vector | scalar | aggregate | reference + + vector := vector_elem width | + vector_elem := 'i' | 'u' | 's' | 'f' + + scalar := scalar_type number + scalar_type := 'U' | 'S' | 'F' + + aggregate := '(' (type),* ')' 'f'? + + reference := number modifiers* + modifiers := 'v' | 'h' | 'd' | 'n' | 'w' | 'u' | 's' | + 'x' number + + + width = number | '(' number '-' number ')' + + number = [0-9]+ + ``` + + ## Vectors + + The vector grammar is a pattern describing many possibilities + for arguments/return value. The `vector_elem` describes the + types of elements to use, and the `width` describes the (range + of) widths for those elements, which are then placed into a + vector with the `width` bitwidth. E.g. if an intrinsic has a + `width` that includes 128, and the return value is `i(8-32)`, + then some instantiation of that intrinsic will be `u8x16`, + `u32x4`, `i32x4`, etc. + + ### Elements + + - i: integer, both signed and unsigned + - u: unsigned integer + - s: signed integer + - f: float + + ## Scalars + + Similar to vectors, but these describe a single concrete type, + not a range. The number is the bitwidth. + + ### Types + + - U: unsigned integer + - S: signed integer + - F: float + + ## Aggregates + + An aggregate is a collection of multiple types; a tuple in + Rust terms, or an unnamed struct in LLVM. The `f` modifiers + forces the tuple to be flattened in the LLVM + intrinsic. E.g. if `llvm.foo` takes `(F32,S32)`: + + - no `f` corresponds to `declare ... @llvm.foo({float, i32})`. + - having an `f` corresponds to `declare ... @llvm.foo(float, i32)`. + + + (Currently aggregates can not contain other aggregates.) + + ## References + + A reference uses the type of another argument, with possible + modifications. The number refers to the type to use, starting + with 0 == return value, 1 == first argument, 2 == second + argument, etc. (Currently only referencing 0, the return + value, is supported.) + + ### Modifiers + + - 'v': put a scalar into a vector of the current width (u32 -> u32x4, when width == 128) + - 'h': half the length of the vector (u32x4 -> u32x2) + - 'd': double the length of the vector (u32x2 -> u32x4) + - 'n': narrow the element of the vector (u32x4 -> u16x4) + - 'w': widen the element of the vector (u16x4 -> u32x4) + - 'u': force an integer (vector or scalar) to be unsigned (i32x4 -> u32x4) + - 's': force an integer (vector or scalar) to be signed (u32x4 -> i32x4) + - 'x' number: force the type to be a vector of bitwidth `number`. + ''')) + parser.add_argument('--format', choices=FORMATS, required=True, + help = 'Output format.') + parser.add_argument('-o', '--out', type=argparse.FileType('w'), default=sys.stdout, + help = 'File to output to (default stdout).') + parser.add_argument('in_', metavar="FILE", type=argparse.FileType('r'), + help = 'JSON file to load') + return parser.parse_args() + + +class ExternBlock(object): + def __init__(self): + pass + def open(self, platform): + return 'extern "platform-intrinsic" {' + def render(self, mono): + return ' fn {}{};'.format(mono.intrinsic_name(), + mono.intrinsic_signature()) + def close(self): + return '}' + +class CompilerDefs(object): + def __init__(self): + pass + def open(self, platform): + return '''\ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// DO NOT EDIT: autogenerated by etc/platform-intrinsics/generator.py +// ignore-tidy-linelength + +use {{Intrinsic, i, u, f, v, agg}}; +use IntrinsicDef::Named; +use rustc::middle::ty; + +pub fn find<'tcx>(_tcx: &ty::ctxt<'tcx>, name: &str) -> Option {{ + if !name.starts_with("{0}") {{ return None }} + Some(match &name["{0}".len()..] {{'''.format(platform.intrinsic_prefix()) + + def render(self, mono): + return '''\ + "{}" => Intrinsic {{ + inputs: vec![{}], + output: {}, + definition: Named("{}") + }},'''.format(mono.intrinsic_suffix(), + mono.compiler_args(), + mono.compiler_ret(), + mono.llvm_name()) + def close(self): + return '''\ + _ => return None, + }) +}''' + +FORMATS = { + 'extern-block': ExternBlock(), + 'compiler-defs': CompilerDefs(), +} + + +def main(): + args = parse_args() + in_ = args.in_ + out = args.out + out_format = FORMATS[args.format] + + intrinsics = IntrinsicSet(json.load(in_)) + + print(out_format.open(intrinsics), file=out) + for intr in intrinsics.intrinsics(): + for mono in intr.monomorphise(): + print(out_format.render(mono), file=out) + print(out_format.close(), file=out) + +if __name__ == '__main__': + main()