forked from intelxed/xed
-
Notifications
You must be signed in to change notification settings - Fork 0
/
xed3_nt.py
executable file
·795 lines (687 loc) · 28.7 KB
/
xed3_nt.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
#BEGIN_LEGAL
#
#Copyright (c) 2018 Intel Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
#END_LEGAL
import genutil
import ildutil
import ild_nt
import ild_cdict
import mbuild
import codegen
import ild_phash
import os
import ild_codegen
import operand_storage
import verbosity
import tup2int
_xed3_ops_type = 'xed3_operands_struct_t*'
_xed3_ops_header = 'xed3-operands-struct.h'
_key_ctype = 'xed_uint32_t'
_xed3_err_op = 'error'
_xed3_gen_error = 'XED_ERROR_GENERAL_ERROR'
_xed_reg_error_val = 'XED_ERROR_BAD_REGISTER'
_xed_no_err_val = 'XED_ERROR_NONE'
_xed_op_type = 'xed_operand_values_t'
_xed3_opstruct_type = 'xed3_operands_struct_t'
def _vlog(f,s):
if verbosity.vcapture():
f.write(s)
#FIXME: this is not used currently, but it would be nice
#to have a special error enum with more specific error reasons
#instead of just XED_ERROR_GENERAL_ERROR
#for example for each NT we can create a separate error value
def _nt_2_xed3_err_enum(nt_name):
return 'XED3_ERROR_%s' % nt_name.upper()
#This is not used currently.
#I was going to use it for decoding the operands: for each
#operand enum value call the corresponding NT function,
#but it turned out to be better to generate chain capture functions
#for each operands combination
def dump_nt_enum_2_capture_fptr(agi, fname):
"""Dump mapping nt_enum -> nt_capture_fptr
"""
xeddir = os.path.abspath(agi.common.options.xeddir)
gendir = mbuild.join(agi.common.options.gendir,'include-private')
h_file = codegen.xed_file_emitter_t(xeddir,gendir,
fname, shell_file=False,
is_private=True)
h_file.add_header('xed-lookup-functions.h')
h_file.add_header(_xed3_nt_capture_header)
h_file.start()
lu_name = 'xed3_nt_2_capture'
xed3_capture_f_t = 'xed3_capture_function_t'
fptr_typedef = 'typedef void(*%s)(%s*);' % (xed3_capture_f_t,
ildutil.xed3_decoded_inst_t)
fptr_typedef = 'typedef void(*%s)(xed_decoded_inst_t*);' % xed3_capture_f_t
h_file.add_code(fptr_typedef)
h_file.add_code(('static %s ' % xed3_capture_f_t) +\
'%s[XED_NONTERMINAL_LAST] = {' % lu_name)
nonterminals = list(agi.nonterminal_dict.keys())
invalid_line = '/*XED_NONTERMINAL_INVALID*/ (%s)0,' % xed3_capture_f_t
h_file.add_code(invalid_line)
for nt_name in list(agi.xed3_nt_enum_val_map.values()):
enum_val = 'XED_NONTERMINAL_%s' % nt_name.upper()
if _skip_nt(nt_name):
fn = '0'
else:
fn = get_xed3_nt_capture_fn(nt_name)
h_file.add_code('/*%s*/ (%s)%s,'% (enum_val, xed3_capture_f_t, fn))
h_file.add_code('};')
h_file.close()
def get_ii_constraints(ii, state_space, constraints):
"""
sets constraints[xed_operand_name][xed_operand_val] = True
xed_operand_name and xed_operand_val correspond to operands
encountered in ii (both operand deciders and constant prebindings)
"""
#set constraints that come from operands deciders
ild_nt.add_op_deciders(ii.ipattern, state_space, constraints)
#set constraints that come from prebindings
for name,binding in list(ii.prebindings.items()):
if binding.is_constant():
if name not in constraints:
constraints[name] = {}
val = int(binding.get_value(), 2)
constraints[name][val] = True
def _get_all_cnames(gi):
"""
Returns a set of all constraints used by a given gi
(generator_info - represents a single NT)
"""
cnames = []
for rule in gi.parser_output.instructions:
cnames.extend(list(rule.xed3_constraints.keys()))
return set(cnames)
def _gen_cdict(agi, nt_name, all_state_space):
"""
Creates a ild_cdict.constraint_dict_t corresponding to NT
defined by gi.
"""
gi = agi.generator_dict[nt_name]
options = agi.common.options
state_space = {}
for opname in all_state_space:
state_space[opname] = list(all_state_space[opname].keys())
cdict_list = []
for rule in gi.parser_output.instructions:
rule.xed3_constraints = {}
get_ii_constraints(rule, state_space, rule.xed3_constraints)
cnames = _get_all_cnames(gi)
for rule in gi.parser_output.instructions:
cdict = ild_cdict.constraint_dict_t(
cnames,
rule.xed3_constraints,
all_state_space,
rule)
cdict_list.append(cdict)
msg = "cdict conflict in NT %s\n" % nt_name
united_dict = ild_cdict.constraint_dict_t.unite_dicts(
cdict_list,
msg,
cnames)
return united_dict
def get_xed3_member_name(xed2_opname):
"""
This is not used currently.
When we have a struct for operands instead of _operands array
this can be used to get struct member name from operand
"""
return xed2_opname.lower()
_xed3_capture_fn_pfx = 'xed3_capture'
def get_xed3_nt_capture_fn(nt_name):
"""
Return a xed3 capture function name for a given NT name.
"""
return '%s_nt_%s' % (_xed3_capture_fn_pfx, nt_name)
def get_xed3_capture_chain_fn(nt_names, is_ntluf=False):
"""
Return a xed3 chain capture function name from a given list of
NT names.
is_ntluf==True for operands chain functions.
"""
suffix = '_'.join(nt_names)
if is_ntluf:
suffix = 'ntluf_%s' % suffix
return '%s_chain_%s' % (_xed3_capture_fn_pfx, suffix)
def _add_cgen_key_lines(fo,
nt_name,
gi,
all_ops_widths,
key_str='key',
inst='d'):
"""
Add C code to compute the key from constraints' values.
"""
fo.add_code_eol('%s %s = 0' % (_key_ctype, key_str))
cdict = gi.xed3_cdict
bit_shift = 0
for i,cname in enumerate(cdict.cnames):
#eosz_set=True indicates that current value of EOSZ is correct
#in the _operands array and we can take it from there.
#Otherwise we would have to use special eosz computing functions
#the same way as we do in ILD.
#eosz_set=True here because we are doing dynamic decoding
#and have processed the NTs that come before the current NT.
access_str = ild_codegen.emit_ild_access_call(cname, inst,
eoasz_set=True)
#constraints might have 1,2 or 3 bit widths
#and we allocate bits in the key vector appropriately
#e.g REXB operand gets only 1 bit in the key
#and RM gets 3 bits
shift_val = ('(%s)' % bit_shift)
bit_shift += all_ops_widths[cname]
fo.add_code_eol('%s += (%s) << (%s)' % (key_str,access_str, shift_val))
def _get_pattern_nts(rule):
"""
Return a list of NT names present in given rule.
"""
nt_names = []
for bt in rule.ipattern.bits:
if bt.is_nonterminal():
nt_name = bt.nonterminal_name()
nt_names.append(nt_name)
return nt_names
def _is_error_rule(rule):
for op in rule.operands:
if op.type == 'error':
return True
return False
def _add_capture_nt_call(fo, nt_name, inst='d', indent=0):
capture_fn = get_xed3_nt_capture_fn(nt_name)
indent = ' ' * indent
fo.add_code_eol('%s%s(%s)' % (indent, capture_fn, inst))
def _add_op_assign_stmt(fo, opname, opval, inst='d', op=None,
indent=0):
if op:
fo.add_code('/* op.type=%s */' % op.type)
setter_fn = operand_storage.get_op_setter_fn(opname)
set_stmt = '%s(%s, %s)' %(setter_fn, inst, opval)
indentstr = ' ' * indent
fo.add_code_eol(indentstr + set_stmt)
def _is_reg_error_op(op):
return op.bits in ['XED_REG_ERROR']
def _add_nt_rhs_assignments(fo, nt_name, gi, rule, inst='d'):
#fo.add_code("/* %s */" % rule)
#first if it's error, we set general_error and quit
if _is_error_rule(rule):
_add_op_assign_stmt(fo, _xed3_err_op, _xed3_gen_error,
inst, indent=1)
return
#now check if there are NT calls in pattern, we need to call them first
pattern_nts = _get_pattern_nts(rule)
for nt_name in pattern_nts:
_add_capture_nt_call(fo, nt_name, inst, indent=1)
#now let's do the RHS - for each operand assign value
#FIXME: if we assign ERROR_REG or INVALID_REG set also error?
for op in rule.operands:
if op.name == 'ENCODER_PREFERRED':
#skip encoder preferred
continue
if op.type == 'imm':
#skip prebindings
continue
if op.type == 'nt_lookup_fn':
#NT as RHS, we call its capturing function
#and then assign op.name to OUTREG
_add_capture_nt_call(fo, op.lookupfn_name, inst, indent=1)
#now copy the outreg to op.name (unless it is outreg too!)
if op.name != 'OUTREG':
getter_fn = operand_storage.get_op_getter_fn('outreg')
outreg_expr = '%s(%s)' % (getter_fn, inst)
_add_op_assign_stmt(fo, op.name, outreg_expr, inst, indent=1)
else: #assignment of an operand to a constant
_add_op_assign_stmt(fo, op.name, op.bits, inst, indent=1)
if _is_reg_error_op(op):
_add_op_assign_stmt(fo, _xed3_err_op,
_xed_reg_error_val, inst, indent=1)
fo.add_code('/*pacify the compiler */')
fo.add_code_eol('(void)%s' % inst)
def _add_case_lines(fo, nt_name, gi, rule, inst='d'):
_add_nt_rhs_assignments(fo, nt_name, gi, rule, inst=inst)
fo.add_code_eol(' break')
def _add_switchcase_lines(fo,
nt_name,
gi,
all_ops_widths,
key_str='key',
inst='d'):
cdict = gi.xed3_cdict
fo.add_code('switch(%s) {' %key_str)
int2key = {}
key2int = {}
for key in list(cdict.tuple2rule.keys()):
keyval = tup2int.tuple2int(key, cdict.cnames, all_ops_widths)
#This checks for a nasty conflict that should never happen:
#when two different tuple keys have the same integer value.
#This conflict can happen when bit widths of all constraints are
#bigger than 32 bit (key is uint32 currently).
#In general such error will be caught by C compiler when we try
#to build a key and shift more than 32 bits.
#Checking here too just to be sure.
#FIXME: add an assertion to constraint_dict_t constructor to check
#for that?
#FIXME: this doesn't really checks for integer overflow, because
#python autmatically extends int32 if it overflows to int64.
#Need better checking.
if keyval in int2key:
msg = []
msg.append('CDICT TUPLE VALUE CONFLICT in nt %s !!!!' % nt_name)
msg.append('keyval %s' % keyval)
msg.append('key1 %s, key2 %s' % (key, int2key[keyval]))
msg.append('cdict %s')
msg = '\n'.join(msg)
ildutil.ild_err(msg)
int2key[keyval] = key
key2int[key] = keyval
covered_rules = set()
#we want cases sorted by value - prettier
for keyval in sorted(int2key.keys()):
key = int2key[keyval]
rule = cdict.tuple2rule[key]
if rule in covered_rules:
continue
covered_rules.add(rule)
keys = cdict.get_all_keys_by_val(rule)
for key in keys:
#FIXME: move tuple2int to ild_cdict?
keyval = key2int[key]
fo.add_code('case %s: /*%s -> %s*/' %(keyval, key, rule))
_add_case_lines(fo, nt_name, gi, rule)
fo.add_code('default:')
if gi.parser_output.otherwise_ok:
fo.add_code('/* otherwise_ok */')
else:
#FIXME: temporary using general error, later
#define more specific error enum
#errval = _nt_2_xed3_err_enum(nt_name)
errval = 'XED_ERROR_GENERAL_ERROR'
_add_op_assign_stmt(fo, _xed3_err_op, errval,
inst, indent=1)
fo.add_code_eol(' break')
fo.add_code('}')
def gen_capture_fo(agi, nt_name, all_ops_widths):
"""
Generate xed3 capturing function for a given NT name.
"""
gi = agi.generator_dict[nt_name]
cdict = gi.xed3_cdict
fname = get_xed3_nt_capture_fn(nt_name)
inst = 'd'
keystr = 'key'
fo = fo = codegen.function_object_t(fname,
return_type='void',
static=True,
inline=True)
fo.add_arg(ildutil.xed3_decoded_inst_t + '* %s' % inst)
if len(cdict.cnames) > 0:
_add_cgen_key_lines(fo, nt_name, gi, all_ops_widths, keystr, inst)
fo.add_code('/* now switch code..*/')
_add_switchcase_lines(fo, nt_name, gi, all_ops_widths, keystr, inst)
else:
rule = cdict.rule
_add_nt_rhs_assignments(fo, nt_name, gi, rule)
return fo
#FIXME: not used currently.
#Will be nice to use it and have different types for different members,
#especially for registers. 9debugging and overall tidy code)
def _gen_xed3_op_struct(agi, hfn):
"""
Dump xed3_oprands_struct_t definition
"""
xeddir = os.path.abspath(agi.common.options.xeddir)
gendir = mbuild.join(agi.common.options.gendir)
h_file = codegen.xed_file_emitter_t(xeddir,gendir,
hfn, shell_file=False,
is_private=False)
h_file.add_header('xed-operand-storage.h')
h_file.start()
typedef_s = 'typedef struct xed3_operands_struct_s {'
h_file.add_code(typedef_s)
for op_name in agi.xed3_operand_names:
h_file.add_code('%s %s;'% (_xed_op_type, op_name.lower()))
h_file.add_code('} %s;' %_xed3_opstruct_type)
h_file.close()
def _get_op_nt_names_from_ii(ii):
nt_names = []
for op in ii.operands:
if op.type == 'nt_lookup_fn':
nt_names.append(op.name + '_' + op.lookupfn_name)
elif op.type == 'imm_const':
suffix = '_const%s' % op.bits
nt_names.append(op.name + suffix)
elif op.type == 'reg':
suffix = '_%s' % op.bits
nt_names.append(op.name + suffix)
return nt_names
def _get_nt_names_from_ii(ii):
"""
@param ii - instruction_info_t
@return list of NT names in ii's pattern
"""
nt_names = []
for bt in ii.ipattern.bits:
if bt.is_nonterminal():
name = bt.nonterminal_name()
if not name:
ildutil.ild_err('Failed to get NT name in %s for %s' % (ii,bt))
nt_names.append(name)
return nt_names
def _gen_ntluf_capture_chain_fo(nt_names, ii):
"""
Given a list of OP_NAME_NT_NAME strings(nt_names), generate a function
object (function_object_t)
that calls corresponding xed3 NT capturing functions.
Each such function captures everything that xed2 decode graph would
capture for a given pattern with operands that have nt_lokkupfns.
The difference between this function and _gen_capture_chain_fo
is that this function creates chain capturing functions for
operand decoding - assigns the REG[0,1] operands, etc.
"""
fname = get_xed3_capture_chain_fn(nt_names, is_ntluf=True)
inst = 'd'
fo = fo = codegen.function_object_t(fname,
return_type=_xed3_chain_return_t,
static=True,
inline=True)
fo.add_arg(ildutil.xed3_decoded_inst_t + '* %s' % inst)
for op in ii.operands:
if op.type == 'nt_lookup_fn':
nt_name = op.lookupfn_name
capture_fn = get_xed3_nt_capture_fn(nt_name)
capture_stmt = '%s(%s)' % (capture_fn, inst)
fo.add_code_eol(capture_stmt)
#if we have NTLUF functions, we need to assign OUTREG
getter_fn = operand_storage.get_op_getter_fn('outreg')
outreg_expr = '%s(%s)' % (getter_fn, inst)
fo.add_code('/*opname %s */' % op.name)
_add_op_assign_stmt(fo, op.name, outreg_expr, inst)
#now check if we have errors in current NT
#we don't need to check if there was a reg_error because
#we assign error operand inside the called nt_capture function
#if there was a reg_error
getter_fn = operand_storage.get_op_getter_fn(_xed3_err_op)
errval = '%s(%s)' % (getter_fn, inst)
fo.add_code('if (%s) {' % errval)
fo.add_code_eol('return %s' % errval)
fo.add_code('}')
elif op.type in ['imm_const', 'reg']:
opval = op.bits
_add_op_assign_stmt(fo, op.name, opval, inst)
fo.add_code_eol('return %s' % _xed_no_err_val)
return fo
def _gen_capture_chain_fo(nt_names, fname=None):
"""
Given a list of NT names, generate a function object (function_object_t)
that calls corresponding xed3 NT capturing functions.
Each such function captures everything that xed2 decode graph would
capture for a given pattern with NTs (nt_names) in it.
"""
if not fname:
fname = get_xed3_capture_chain_fn(nt_names)
inst = 'd'
fo = fo = codegen.function_object_t(fname,
return_type=_xed3_chain_return_t,
static=True,
inline=True)
fo.add_arg(ildutil.xed3_decoded_inst_t + '* %s' % inst)
for name in nt_names:
capture_fn = get_xed3_nt_capture_fn(name)
capture_stmt = '%s(%s)' % (capture_fn, inst)
fo.add_code_eol(capture_stmt)
#now check if we have errors in current NT
getter_fn = operand_storage.get_op_getter_fn(_xed3_err_op)
errval = '%s(%s)' % (getter_fn, inst)
fo.add_code('if (%s) {' % errval)
fo.add_code_eol('return %s' % errval)
fo.add_code('}')
fo.add_code_eol('return %s' % _xed_no_err_val)
return fo
_xed3_chain_header = 'xed3-chain-capture.h'
_xed3_op_chain_header = 'xed3-op-chain-capture.h'
_xed3_chain_lu_header = 'xed3-chain-capture-lu.h'
_xed3_op_chain_lu_header = 'xed3-op-chain-capture-lu.h'
_xed3_nt_capture_header = 'xed3-nt-capture.h'
_xed3_capture_lu_header = 'xed3-nt-capture-lu.h'
_xed3_empty_capture_func = 'xed3_capture_nt_nop'
_xed3_chain_return_t = 'xed_error_enum_t'
_xed3_dynamic_part1_header = 'xed3-dynamic-part1-capture.h'
def _gen_empty_capture_fo(is_ntluf=False):
"""
Generate capture function that does nothing.
For patterns without NTs.
"""
inst = 'd'
if is_ntluf:
fname = '%s_ntluf' % _xed3_empty_capture_func
else:
fname = _xed3_empty_capture_func
fo = fo = codegen.function_object_t(fname,
return_type=_xed3_chain_return_t,
static=True,
inline=True)
fo.add_arg(ildutil.xed3_decoded_inst_t + '* %s' % inst)
fo.add_code_eol('(void)%s' % inst)
fo.add_code_eol('return %s' % _xed_no_err_val)
return fo
def _dump_op_capture_chain_fo_lu(agi, patterns):
"""
Creates chain capturing functions for operands - for each pattern,
dumps those functions definitions, dumps a mapping
from inum(xed_inst_t index) to those functions.
"""
fn_2_fo = {}
inum_2_fn = {}
nop_fo = _gen_empty_capture_fo(is_ntluf=True)
fn_2_fo[nop_fo.function_name] = nop_fo
for ptrn in patterns:
ii = ptrn.ii
nt_names = _get_op_nt_names_from_ii(ii)
if len(nt_names) == 0:
#if no NTs we use empty capturing function
fn = nop_fo.function_name
else:
fn = get_xed3_capture_chain_fn(nt_names, is_ntluf=True)
if fn not in fn_2_fo:
fo = _gen_ntluf_capture_chain_fo(nt_names, ii)
fn_2_fo[fn] = fo
inum_2_fn[ii.inum] = (fn, ii.operands)
#dump chain functions
headers = [_xed3_nt_capture_header]
ild_codegen.dump_flist_2_header(agi,
_xed3_op_chain_header,
headers,
list(fn_2_fo.values()),
is_private=True)
lu_size = max(inum_2_fn.keys()) + 1
xeddir = os.path.abspath(agi.common.options.xeddir)
gendir = mbuild.join(agi.common.options.gendir,'include-private')
h_file = codegen.xed_file_emitter_t(xeddir,gendir,
_xed3_op_chain_lu_header, shell_file=False,
is_private=True)
h_file.add_header(_xed3_op_chain_header)
h_file.start()
lu_name = 'xed3_op_chain_fptr_lu'
xed3_op_chain_f_t = 'xed3_op_chain_function_t'
fptr_typedef = 'typedef %s(*%s)(%s*);' % (_xed3_chain_return_t,
xed3_op_chain_f_t,
ildutil.xed3_decoded_inst_t)
h_file.add_code(fptr_typedef)
h_file.add_code(('static %s ' % xed3_op_chain_f_t) +\
'%s[%s] = {' % (lu_name, lu_size))
empty_line = '/*NO PATTERN*/ (%s)0,' % xed3_op_chain_f_t
for inum in range(0, lu_size):
if inum in inum_2_fn:
(fn, oplist) = inum_2_fn[inum]
op_str = '\n'.join([str(op) for op in oplist])
entry_str = '/*%s inum=%s*/ %s,' % (op_str,inum, fn)
else:
entry_str = empty_line
h_file.add_code(entry_str)
h_file.add_code('};')
h_file.close()
def _dump_capture_chain_fo_lu(agi, patterns):
"""
Creates chain capturing functions - for each pattern,
dumps those functions definitions, dumps a mapping
from inum(xed_inst_t index) to those functions.
"""
fn_2_fo = {}
inum_2_fn = {}
nop_fo = _gen_empty_capture_fo()
fn_2_fo[nop_fo.function_name] = nop_fo
for ptrn in patterns:
ii = ptrn.ii
nt_names = _get_nt_names_from_ii(ii)
if len(nt_names) == 0:
#if no NTs we use empty capturing function
fn = nop_fo.function_name
else:
fn = get_xed3_capture_chain_fn(nt_names)
if fn not in fn_2_fo:
fo = _gen_capture_chain_fo(nt_names)
fn_2_fo[fn] = fo
inum_2_fn[ii.inum] = (fn, ptrn.ptrn)
#dump chain functions
headers = [_xed3_nt_capture_header]
ild_codegen.dump_flist_2_header(agi,
_xed3_chain_header,
headers,
list(fn_2_fo.values()),
is_private=True)
lu_size = max(inum_2_fn.keys()) + 1
xeddir = os.path.abspath(agi.common.options.xeddir)
gendir = mbuild.join(agi.common.options.gendir,'include-private')
h_file = codegen.xed_file_emitter_t(xeddir,gendir,
_xed3_chain_lu_header, shell_file=False,
is_private=True)
h_file.add_header(_xed3_chain_header)
h_file.start()
lu_name = 'xed3_chain_fptr_lu'
xed3_chain_f_t = 'xed3_chain_function_t'
fptr_typedef = 'typedef %s(*%s)(%s*);' % (_xed3_chain_return_t,
xed3_chain_f_t,
ildutil.xed3_decoded_inst_t)
h_file.add_code(fptr_typedef)
h_file.add_code(('static %s ' % xed3_chain_f_t) +\
'%s[%s] = {' % (lu_name, lu_size))
empty_line = '/*NO PATTERN*/ (%s)0,' % xed3_chain_f_t
for inum in range(0, lu_size):
if inum in inum_2_fn:
(fn, ptrn_str) = inum_2_fn[inum]
entry_str = '/*\n%s\ninum=%s*/ %s,' % (ptrn_str, inum,fn)
else:
entry_str = empty_line
h_file.add_code(entry_str)
h_file.add_code('};')
h_file.close()
def _dump_dynamic_part1_f(agi):
"""
Dumps the xed3_dynamic_decode_part1 function that captures all the
NTs in the spine that come before INSTRUCTIONS NT.
"""
fo = _gen_dynamic_part1_fo(agi)
#dump the function
headers = [_xed3_nt_capture_header]
ild_codegen.dump_flist_2_header(agi,
_xed3_dynamic_part1_header,
headers,
[fo],
is_private=True)
#things that are covered by ILD and static decoding
#FIXME: is there a better way to determine them instead of just
#hardcoding their names?
_nts_to_skip = ['PREFIXES', 'ISA']
_spine_nt_name = 'ISA'
_dynamic_part1_fn = 'xed3_dynamic_decode_part1'
def _skip_nt(nt_name):
"""
Return True if there is no need to generate a capturing
function for a given NT name.
"""
return (nt_name in _nts_to_skip or
'INSTRUCTIONS' in nt_name or
'SPLITTER' in nt_name)
def _gen_dynamic_part1_fo(agi):
"""
Generate the xed3_dynamic_decode_part1 function that
captures all the NTs that come before INSTRUCTIONS.
The generated function should be called after ILD and before
static decoding.
"""
gi = agi.generator_dict[_spine_nt_name]
if len(gi.parser_output.instructions) != 1:
ildutil.ild_err("Failed to gen dynamic part1 function!\n" +
"Unexpected number of rules in %s NT: %s" %
(_spine_nt_name, len(gi.parser_output.instructions)))
rule = gi.parser_output.instructions[0]
nt_names = _get_nt_names_from_ii(rule)
#filter NTs that we want to skip
nt_names = list(filter(lambda x: not _skip_nt(x), nt_names))
fo = _gen_capture_chain_fo(nt_names, fname=_dynamic_part1_fn)
return fo
def work(agi, all_state_space, all_ops_widths, patterns):
"""
Main entry point of the module.
For each NT generate a capturing function.
Then for each sequence of NTs in patterns generate a
chain capturing function that would call single capturing functions
for each NT.
Then for each combination of operands generate operands chain captuirng
function.
Also generate lookup tables to obtain those chain capturing functions
from inum (xed_inst_t index).
"""
gendir = ild_gendir = agi.common.options.gendir
logfn = mbuild.join(gendir, 'xed3_nt_cdicts.txt')
log_f = open(logfn, 'w')
#generate NT capturing functions
capture_fn_list = []
for nt_name in list(agi.nonterminal_dict.keys()):
#skip nonterminals that we don't want to capture:
#PREFIXES, AVX_SPLITTER, *ISA, etc.
if _skip_nt(nt_name):
continue
_vlog(log_f,'processing %s\n' % nt_name)
#create a constraint_dict_t for each NT
nt_cdict = _gen_cdict(agi, nt_name, all_state_space)
_vlog(log_f,'NT:%s:\n%s\n' % (nt_name, nt_cdict))
gi = agi.generator_dict[nt_name]
gi.xed3_cdict = nt_cdict #just for transporting
#create a function_object_t for the NT
fo = gen_capture_fo(agi, nt_name, all_ops_widths)
gi.xed3_capture_fo = fo
capture_fn_list.append(fo)
_vlog(log_f,fo.emit())
#dump NT capturing functions
headers = [operand_storage.get_operand_accessors_fn(), ildutil.ild_header]
ild_codegen.dump_flist_2_header(agi,
_xed3_nt_capture_header,
headers,
capture_fn_list,
is_private=True)
#in each pattern we have a number of NTs,
#now that we have a capturing function for each NT
#we can create a create a function that would call
#needed capturing functions for each pattern.
#Also dump lookup tables from inum(xed_inst_t index) to
#chain capturing functions
_dump_capture_chain_fo_lu(agi, patterns)
#do the same for operands of each xed_inst_t
_dump_op_capture_chain_fo_lu(agi, patterns)
#create chain capturing functions for the NTs that come from
#spine, before the INSTRUCTIONS NT
_dump_dynamic_part1_f(agi)
log_f.close()