1# Copyright (C) 2014-2018 Intel Corporation.   All Rights Reserved.
2#
3# Permission is hereby granted, free of charge, to any person obtaining a
4# copy of this software and associated documentation files (the "Software"),
5# to deal in the Software without restriction, including without limitation
6# the rights to use, copy, modify, merge, publish, distribute, sublicense,
7# and/or sell copies of the Software, and to permit persons to whom the
8# Software is furnished to do so, subject to the following conditions:
9#
10# The above copyright notice and this permission notice (including the next
11# paragraph) shall be included in all copies or substantial portions of the
12# Software.
13#
14# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20# IN THE SOFTWARE.
21
22from __future__ import print_function
23import os, sys, re
24from gen_common import *
25from argparse import FileType
26
27inst_aliases = {
28    'SHUFFLE_VECTOR': 'VSHUFFLE',
29    'INSERT_ELEMENT': 'VINSERT',
30    'EXTRACT_ELEMENT': 'VEXTRACT',
31    'MEM_SET': 'MEMSET',
32    'MEM_CPY': 'MEMCOPY',
33    'MEM_MOVE': 'MEMMOVE',
34    'L_SHR': 'LSHR',
35    'A_SHR': 'ASHR',
36    'BIT_CAST': 'BITCAST',
37    'U_DIV': 'UDIV',
38    'S_DIV': 'SDIV',
39    'U_REM': 'UREM',
40    'S_REM': 'SREM',
41    'BIN_OP': 'BINOP',
42}
43
44intrinsics = [
45    ['VGATHERPD',   ['src', 'pBase', 'indices', 'mask', 'scale'], 'src'],
46    ['VGATHERPS',   ['src', 'pBase', 'indices', 'mask', 'scale'], 'src'],
47    ['VGATHERDD',   ['src', 'pBase', 'indices', 'mask', 'scale'], 'src'],
48    ['VSCATTERPS',  ['pBase', 'mask', 'indices', 'src', 'scale'], 'src'],
49    ['VRCPPS',      ['a'], 'a'],
50    ['VROUND',      ['a', 'rounding'], 'a'],
51    ['BEXTR_32',    ['src', 'control'], 'src'],
52    ['VPSHUFB',     ['a', 'b'], 'a'],
53    ['VPERMD',      ['a', 'idx'], 'a'],
54    ['VPERMPS',     ['idx', 'a'], 'a'],
55    ['VCVTPD2PS',   ['a'], 'getVectorType(mFP32Ty, VEC_GET_NUM_ELEMS)'],
56    ['VCVTPS2PH',   ['a', 'round'], 'mSimdInt16Ty'],
57    ['VHSUBPS',     ['a', 'b'], 'a'],
58    ['VPTESTC',     ['a', 'b'], 'mInt32Ty'],
59    ['VPTESTZ',     ['a', 'b'], 'mInt32Ty'],
60    ['VPHADDD',     ['a', 'b'], 'a'],
61    ['PDEP32',      ['a', 'b'], 'a'],
62    ['RDTSC',       [], 'mInt64Ty'],
63]
64
65llvm_intrinsics = [
66    ['CTTZ', 'cttz', ['a', 'flag'], ['a']],
67    ['CTLZ', 'ctlz', ['a', 'flag'], ['a']],
68    ['VSQRTPS', 'sqrt', ['a'], ['a']],
69    ['STACKSAVE', 'stacksave', [], []],
70    ['STACKRESTORE', 'stackrestore', ['a'], []],
71    ['VMINPS', 'minnum', ['a', 'b'], ['a']],
72    ['VMAXPS', 'maxnum', ['a', 'b'], ['a']],
73    ['VFMADDPS', 'fmuladd', ['a', 'b', 'c'], ['a']],
74    ['DEBUGTRAP', 'debugtrap', [], []],
75    ['POPCNT', 'ctpop', ['a'], ['a']],
76    ['LOG2', 'log2', ['a'], ['a']],
77    ['FABS', 'fabs', ['a'], ['a']],
78    ['EXP2', 'exp2', ['a'], ['a']],
79    ['COS', 'cos', ['a'], ['a']],
80    ['SIN', 'sin', ['a'], ['a']],
81    ['FLOOR', 'floor', ['a'], ['a']],
82    ['POW', 'pow', ['a', 'b'], ['a']]
83]
84
85this_dir = os.path.dirname(os.path.abspath(__file__))
86template = os.path.join(this_dir, 'templates', 'gen_builder.hpp')
87
88def convert_uppercamel(name):
89    s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name)
90    return re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).upper()
91
92'''
93    Given an input file (e.g. IRBuilder.h) generates function dictionary.
94'''
95def parse_ir_builder(input_file):
96
97    functions = []
98
99    lines = input_file.readlines()
100    deprecated = None
101
102    idx = 0
103    while idx < len(lines) - 1:
104        line = lines[idx].rstrip()
105        idx += 1
106
107        if deprecated is None:
108            deprecated = re.search(r'LLVM_ATTRIBUTE_DEPRECATED', line)
109
110        #match = re.search(r'\*Create', line)
111        match = re.search(r'[\*\s]Create(\w*)\(', line)
112        if match is not None:
113            #print('Line: %s' % match.group(1))
114
115            # Skip function if LLVM_ATTRIBUTE_DEPRECATED found before
116            if deprecated is not None:
117                deprecated = None
118                continue
119
120            if re.search(r'^\s*Create', line) is not None:
121                func_sig = lines[idx-2].rstrip() + line
122            else:
123                func_sig = line
124
125            end_of_args = False
126            while not end_of_args:
127                end_paren = re.search(r'\)', line)
128                if end_paren is not None:
129                    end_of_args = True
130                else:
131                    line = lines[idx].rstrip()
132                    func_sig += line
133                    idx += 1
134
135            delfunc = re.search(r'LLVM_DELETED_FUNCTION|= delete;', func_sig)
136
137            if not delfunc:
138                func = re.search(r'(.*?)\*[\n\s]*(Create\w*)\((.*?)\)', func_sig)
139                if func is not None:
140
141                    return_type = func.group(1).strip() + '*'
142                    func_name = func.group(2)
143                    arguments = func.group(3)
144
145                    func_args = []
146                    arg_names = []
147                    args = arguments.split(',')
148                    for arg in args:
149                        arg = arg.strip()
150                        if arg:
151                            func_args.append(arg)
152
153                            split_args = arg.split('=')
154                            arg_name = split_args[0].rsplit(None, 1)[-1]
155
156                            reg_arg = re.search(r'[\&\*]*(\w*)', arg_name)
157                            if reg_arg:
158                                arg_names += [reg_arg.group(1)]
159
160                    ignore = False
161
162                    # The following functions need to be ignored in openswr.
163                    # API change in llvm-5.0 breaks baked autogen files
164                    if (
165                        (func_name == 'CreateFence' or
166                         func_name == 'CreateAtomicCmpXchg' or
167                         func_name == 'CreateAtomicRMW')):
168                        ignore = True
169
170                    # The following functions need to be ignored.
171                    if (func_name == 'CreateInsertNUWNSWBinOp' or
172                        func_name == 'CreateMaskedIntrinsic' or
173                        func_name == 'CreateAlignmentAssumptionHelper' or
174                        func_name == 'CreateGEP' or
175                        func_name == 'CreateLoad' or
176                        func_name == 'CreateMaskedLoad' or
177                        func_name == 'CreateStore' or
178                        func_name == 'CreateMaskedStore' or
179                        func_name == 'CreateFCmpHelper' or
180                        func_name == 'CreateElementUnorderedAtomicMemCpy'):
181                        ignore = True
182
183                    # Convert CamelCase to CAMEL_CASE
184                    func_mod = re.search(r'Create(\w*)', func_name)
185                    if func_mod:
186                        func_mod = func_mod.group(1)
187                        func_mod = convert_uppercamel(func_mod)
188                        if func_mod[0:2] == 'F_' or func_mod[0:2] == 'I_':
189                            func_mod = func_mod[0] + func_mod[2:]
190
191                    # Substitute alias based on CAMEL_CASE name.
192                    func_alias = inst_aliases.get(func_mod)
193                    if not func_alias:
194                        func_alias = func_mod
195
196                        if func_name == 'CreateCall' or func_name == 'CreateGEP':
197                            arglist = re.search(r'ArrayRef', ', '.join(func_args))
198                            if arglist:
199                                func_alias = func_alias + 'A'
200
201                    if not ignore:
202                        functions.append({
203                                'name'      : func_name,
204                                'alias'     : func_alias,
205                                'return'    : return_type,
206                                'args'      : ', '.join(func_args),
207                                'arg_names' : arg_names,
208                            })
209
210    return functions
211
212'''
213    Auto-generates macros for LLVM IR
214'''
215def generate_gen_h(functions, output_dir):
216    filename = 'gen_builder.hpp'
217    output_filename = os.path.join(output_dir, filename)
218
219    templfuncs = []
220    for func in functions:
221        decl = '%s %s(%s)' % (func['return'], func['alias'], func['args'])
222
223        templfuncs.append({
224            'decl'      : decl,
225            'intrin'    : func['name'],
226            'args'      : func['arg_names'],
227        })
228
229    MakoTemplateWriter.to_file(
230        template,
231        output_filename,
232        cmdline=sys.argv,
233        comment='Builder IR Wrappers',
234        filename=filename,
235        functions=templfuncs,
236        isX86=False, isIntrin=False)
237
238'''
239    Auto-generates macros for LLVM IR
240'''
241def generate_meta_h(output_dir):
242    filename = 'gen_builder_meta.hpp'
243    output_filename = os.path.join(output_dir, filename)
244
245    functions = []
246    for inst in intrinsics:
247        name = inst[0]
248        args = inst[1]
249        ret = inst[2]
250
251        #print('Inst: %s, x86: %s numArgs: %d' % (inst[0], inst[1], len(inst[2])))
252        if len(args) != 0:
253            declargs = 'Value* ' + ', Value* '.join(args)
254            decl = 'Value* %s(%s, const llvm::Twine& name = "")' % (name, declargs)
255        else:
256            decl = 'Value* %s(const llvm::Twine& name = "")' % (name)
257
258        # determine the return type of the intrinsic. It can either be:
259        # - type of one of the input arguments
260        # - snippet of code to set the return type
261
262        if ret in args:
263            returnTy = ret + '->getType()'
264        else:
265            returnTy = ret
266
267        functions.append({
268            'decl'      : decl,
269            'name'      : name,
270            'args'      : args,
271            'returnType': returnTy
272        })
273
274    MakoTemplateWriter.to_file(
275        template,
276        output_filename,
277        cmdline=sys.argv,
278        comment='meta intrinsics',
279        filename=filename,
280        functions=functions,
281        isX86=True, isIntrin=False)
282
283def generate_intrin_h(output_dir):
284    filename = 'gen_builder_intrin.hpp'
285    output_filename = os.path.join(output_dir, filename)
286
287    functions = []
288    for inst in llvm_intrinsics:
289        #print('Inst: %s, x86: %s numArgs: %d' % (inst[0], inst[1], len(inst[2])))
290        if len(inst[2]) != 0:
291            declargs = 'Value* ' + ', Value* '.join(inst[2])
292            decl = 'Value* %s(%s, const llvm::Twine& name = "")' % (inst[0], declargs)
293        else:
294            decl = 'Value* %s(const llvm::Twine& name = "")' % (inst[0])
295
296        functions.append({
297            'decl'      : decl,
298            'intrin'    : inst[1],
299            'args'      : inst[2],
300            'types'     : inst[3],
301        })
302
303    MakoTemplateWriter.to_file(
304        template,
305        output_filename,
306        cmdline=sys.argv,
307        comment='llvm intrinsics',
308        filename=filename,
309        functions=functions,
310        isX86=False, isIntrin=True)
311'''
312    Function which is invoked when this script is started from a command line.
313    Will present and consume a set of arguments which will tell this script how
314    to behave
315'''
316def main():
317
318    # Parse args...
319    parser = ArgumentParser()
320    parser.add_argument('--input', '-i', type=FileType('r'), help='Path to IRBuilder.h', required=False)
321    parser.add_argument('--output-dir', '-o', action='store', dest='output', help='Path to output directory', required=True)
322    parser.add_argument('--gen_h', help='Generate builder_gen.h', action='store_true', default=False)
323    parser.add_argument('--gen_meta_h', help='Generate meta intrinsics. No input is needed.', action='store_true', default=False)
324    parser.add_argument('--gen_intrin_h', help='Generate llvm intrinsics. No input is needed.', action='store_true', default=False)
325    args = parser.parse_args()
326
327    if not os.path.exists(args.output):
328        os.makedirs(args.output)
329
330    final_output_dir = args.output
331    args.output = MakeTmpDir('_codegen')
332
333    rval = 0
334    try:
335        if args.input:
336            functions = parse_ir_builder(args.input)
337
338            if args.gen_h:
339                generate_gen_h(functions, args.output)
340
341        elif args.gen_h:
342            print('Need to specify --input for --gen_h!')
343
344        if args.gen_meta_h:
345            generate_meta_h(args.output)
346
347        if args.gen_intrin_h:
348            generate_intrin_h(args.output)
349
350        rval = CopyDirFilesIfDifferent(args.output, final_output_dir)
351
352    except:
353        print('ERROR: Could not generate llvm_ir_macros', file=sys.stderr)
354        rval = 1
355
356    finally:
357        DeleteDirTree(args.output)
358
359    return rval
360
361if __name__ == '__main__':
362    sys.exit(main())
363# END OF FILE
364