summaryrefslogtreecommitdiffstats
path: root/src/vppinfra/asm_x86.c
blob: e6e00ce55439cdee7d9be84191c0a6460e847324 (plain)
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
#!/usr/bin/env python
# IPFIX support for Scapy (RFC7011)

from scapy.all import *


# IPFIX Information Elements http://www.iana.org/assignments/ipfix/ipfix.xhtml
information_elements = {
    1:   "octetDeltaCount",
    2:   "packetDeltaCount",
    3:   "deltaFlowCount",
    4:   "protocolIdentifier",
    5:   "ipClassOfService",
    6:   "tcpControlBits",
    7:   "sourceTransportPort",
    8:   "sourceIPv4Address",
    9:   "sourceIPv4PrefixLength",
    10:  "ingressInterface",
    11:  "destinationTransportPort",
    12:  "destinationIPv4Address",
    13:  "destinationIPv4PrefixLength",
    14:  "egressInterface",
    15:  "ipNextHopIPv4Address",
    16:  "bgpSourceAsNumber",
    17:  "bgpDestinationAsNumber",
    18:  "bgpNextHopIPv4Address",
    19:  "postMCastPacketDeltaCount",
    20:  "postMCastOctetDeltaCount",
    21:  "flowEndSysUpTime",
    22:  "flowStartSysUpTime",
    23:  "postOctetDeltaCount",
    24:  "postPacketDeltaCount",
    25:  "minimumIpTotalLength",
    26:  "maximumIpTotalLength",
    27:  "sourceIPv6Address",
    28:  "destinationIPv6Address",
    29:  "sourceIPv6PrefixLength",
    30:  "destinationIPv6PrefixLength",
    31:  "flowLabelIPv6",
    32:  "icmpTypeCodeIPv4",
    33:  "igmpType",
    34:  "samplingInterval",
    35:  "samplingAlgorithm",
    36:  "flowActiveTimeout",
    37:  "flowIdleTimeout",
    38:  "engineType",
    39:  "engineId",
    40:  "exportedOctetTotalCount",
    41:  "exportedMessageTotalCount",
    42:  "exportedFlowRecordTotalCount",
    43:  "ipv4RouterSc",
    44:  "sourceIPv4Prefix",
    45:  "destinationIPv4Prefix",
    46:  "mplsTopLabelType",
    47:  "mplsTopLabelIPv4Address",
    48:  "samplerId",
    49:  "samplerMode",
    50:  "samplerRandomInterval",
    51:  "classId",
    52:  "minimumTTL",
    53:  "maximumTTL",
    54:  "fragmentIdentification",
    55:  "postIpClassOfService",
    56:  "sourceMacAddress",
    57:  "postDestinationMacAddress",
    58:  "vlanId",
    59:  "postVlanId",
    60:  "ipVersion",
    61:  "flowDirection",
    62:  "ipNextHopIPv6Address",
    63:  "bgpNextHopIPv6Address",
    64:  "ipv6ExtensionHeaders",
    70:  "mplsTopLabelStackSection",
    71:  "mplsLabelStackSection2",
    72:  "mplsLabelStackSection3",
    73:  "mplsLabelStackSection4",
    74:  "mplsLabelStackSection5",
    75:  "mplsLabelStackSection6",
    76:  "mplsLabelStackSection7",
    77:  "mplsLabelStackSection8",
    78:  "mplsLabelStackSection9",
    79:  "mplsLabelStackSection10",
    80:  "destinationMacAddress",
    81:  "postSourceMacAddress",
    82:  "interfaceName",
    83:  "interfaceDescription",
    84:  "samplerName",
    85:  "octetTotalCount",
    86:  "packetTotalCount",
    87:  "flagsAndSamplerId",
    88:  "fragmentOffset",
    89:  "forwardingStatus",
    90:  "mplsVpnRouteDistinguisher",
    91:  "mplsTopLabelPrefixLength",
    92:  "srcTrafficIndex",
    93:  "dstTrafficIndex",
    94:  "applicationDescription",
    95:  "applicationId",
    96:  "applicationName",
    98:  "postIpDiffServCodePoint",
    99:  "multicastReplicationFactor",
    100: "className",
    101: "classificationEngineId",
    102: "layer2packetSectionOffset",
    103: "layer2packetSectionSize",
    104: "layer2packetSectionData",
    128: "bgpNextAdjacentAsNumber",
    129: "bgpPrevAdjacentAsNumber",
    130: "exporterIPv4Address",
    131: "exporterIPv6Address",
    132: "droppedOctetDeltaCount",
    133: "droppedPacketDeltaCount",
    134: "droppedOctetTotalCount",
    135: "droppedPacketTotalCount",
    136: "flowEndReason",
    137: "commonPropertiesId",
    138: "observationPointId",
    139: "icmpTypeCodeIPv6",
    140: "mplsTopLabelIPv6Address",
    141: "lineCardId",
    142: "portId",
    143: "meteringProcessId",
    144: "exportingProcessId",
    145: "templateId",
    146: "wlanChannelId",
    147: "wlanSSID",
    148: "flowId",
    149: "observationDomainId",
    150: "flowStartSeconds",
    151: "flowEndSeconds",
    152: "flowStartMilliseconds",
    153: "flowEndMilliseconds",
    154: "flowStartMicroseconds",
    155: "flowEndMicroseconds",
    156: "flowStartNanoseconds",
    157: "flowEndNanoseconds",
    158: "flowStartDeltaMicroseconds",
    159: "flowEndDeltaMicroseconds",
    160: "systemInitTimeMilliseconds",
    161: "flowDurationMilliseconds",
    162: "flowDurationMicroseconds",
    163: "observedFlowTotalCount",
    164: "ignoredPacketTotalCount",
    165: "ignoredOctetTotalCount",
    166: "notSentFlowTotalCount",
    167: "notSentPacketTotalCount",
    168: "notSentOctetTotalCount",
    169: "destinationIPv6Prefix",
    170: "sourceIPv6Prefix",
    171: "postOctetTotalCount",
    172: "postPacketTotalCount",
    173: "flowKeyIndicator",
    174: "postMCastPacketTotalCount",
    175: "postMCastOctetTotalCount",
    176: "icmpTypeIPv4",
    177: "icmpCodeIPv4",
    178: "icmpTypeIPv6",
    179: "icmpCodeIPv6",
    180: "udpSourcePort",
    181: "udpDestinationPort",
    182: "tcpSourcePort",
    183: "tcpDestinationPort",
    184: "tcpSequenceNumber",
    185: "tcpAcknowledgementNumber",
    186: "tcpWindowSize",
    187: "tcpUrgentPointer",
    188: "tcpHeaderLength",
    189: "ipHeaderLength",
    190: "totalLengthIPv4",
    191: "payloadLengthIPv6",
    192: "ipTTL",
    193: "nextHeaderIPv6",
    194: "mplsPayloadLength",
    195: "ipDiffServCodePoint",
    196: "ipPrecedence",
    197: "fragmentFlags",
    198: "octetDeltaSumOfSquares",
    199: "octetTotalSumOfSquares",
    200: "mplsTopLabelTTL",
    201: "mplsLabelStackLength",
    202: "mplsLabelStackDepth",
    203: "mplsTopLabelExp",
    204: "ipPayloadLength",
    205: "udpMessageLength",
    206: "isMulticast",
    207: "ipv4IHL",
    208: "ipv4Options",
    209: "tcpOptions",
    210: "paddingOctets",
    211: "collectorIPv4Address",
    212: "collectorIPv6Address",
    213: "exportInterface",
    214: "exportProtocolVersion",
    215: "exportTransportProtocol",
    216: "collectorTransportPort",
    217: "exporterTransportPort",
    218: "tcpSynTotalCount",
    219: "tcpFinTotalCount",
    220: "tcpRstTotalCount",
    221: "tcpPshTotalCount",
    222: "tcpAckTotalCount",
    223: "tcpUrgTotalCount",
    224: "ipTotalLength",
    225: "postNATSourceIPv4Address",
    226: "postNATDestinationIPv4Address",
    227: "postNAPTSourceTransportPort",
    228: "postNAPTDestinationTransportPort",
    229: "natOriginatingAddressRealm",
    230: "natEvent",
    231: "initiatorOctets",
    232: "responderOctets",
    233: "firewallEvent",
    234: "ingressVRFID",
    235: "egressVRFID",
    236: "VRFname",
    237: "postMplsTopLabelExp",
    238: "tcpWindowScale",
    239: "biflowDirection",
    240: "ethernetHeaderLength",
    241: "ethernetPayloadLength",
    242: "ethernetTotalLength",
    243: "dot1qVlanId",
    244: "dot1qPriority",
    245: "dot1qCustomerVlanId",
    246: "dot1qCustomerPriority",
    247: "metroEvcId",
    248: "metroEvcType",
    249: "pseudoWireId",
    250: "pseudoWireType",
    251: "pseudoWireControlWord",
    252: "ingressPhysicalInterface",
    253: "egressPhysicalInterface",
    254: "postDot1qVlanId",
    255: "postDot1qCustomerVlanId",
    256: "ethernetType",
    257: "postIpPrecedence",
    258: "collectionTimeMilliseconds",
    259: "exportSctpStreamId",
    260: "maxExportSeconds",
    261: "maxFlowEndSeconds",
    262: "messageMD5Checksum",
    263: "messageScope",
    264: "minExportSeconds",
    265: "minFlowStartSeconds",
    266: "opaqueOctets",
    267: "sessionScope",
    268: "maxFlowEndMicroseconds",
    269: "maxFlowEndMilliseconds",
    270: "maxFlowEndNanoseconds",
    271: "minFlowStartMicroseconds",
    272: "minFlowStartMilliseconds",
    273: "minFlowStartNanoseconds",
    274: "collectorCertificate",
    275: "exporterCertificate",
    276: "dataRecordsReliability",
    277: "observationPointType",
    278: "newConnectionDeltaCount",
    279: "connectionSumDurationSeconds",
    280: "connectionTransactionId",
    281: "postNATSourceIPv6Address",
    282: "postNATDestinationIPv6Address",
    283: "natPoolId",
    284: "natPoolName",
    285: "anonymizationFlags",
    286: "anonymizationTechnique",
    287: "informationElementIndex",
    288: "p2pTechnology",
    289: "tunnelTechnology",
    290: "encryptedTechnology",
    291: "basicList",
    292: "subTemplateList",
    293: "subTemplateMultiList",
    294: "bgpValidityState",
    295: "IPSecSPI",
    296: "greKey",
    297: "natType",
    298: "initiatorPackets",
    299: "responderPackets",
    300: "observationDomainName",
    301: "selectionSequenceId",
    302: "selectorId",
    303: "informationElementId",
    304: "selectorAlgorithm",
    305: "samplingPacketInterval",
    306: "samplingPacketSpace",
    307: "samplingTimeInterval",
    308: "samplingTimeSpace",
    309: "samplingSize",
    310: "samplingPopulation",
    311: "samplingProbability",
    312: "dataLinkFrameSize",
    313: "ipHeaderPacketSection",
    314: "ipPayloadPacketSection",
    315: "dataLinkFrameSection",
    316: "mplsLabelStackSection",
    317: "mplsPayloadPacketSection",
    318: "selectorIdTotalPktsObserved",
    319: "selectorIdTotalPktsSelected",
    320: "absoluteError",
    321: "relativeError",
    322: "observationTimeSeconds",
    323: "observationTimeMilliseconds",
    324: "observationTimeMicroseconds",
    325: "observationTimeNanoseconds",
    326: "digestHashValue",
    327: "hashIPPayloadOffset",
    328: "hashIPPayloadSize",
    329: "hashOutputRangeMin",
    330: "hashOutputRangeMax",
    331: "hashSelectedRangeMin",
    332: "hashSelectedRangeMax",
    333: "hashDigestOutput",
    334: "hashInitialiserValue",
    335: "selectorName",
    336: "upperCILimit",
    337: "lowerCILimit",
    338: 
@media only all and (prefers-color-scheme: dark) {
.highlight .hll { background-color: #49483e }
.highlight .c { color: #75715e } /* Comment */
.highlight .err { color: #960050; background-color: #1e0010 } /* Error */
.highlight .k { color: #66d9ef } /* Keyword */
.highlight .l { color: #ae81ff } /* Literal */
.highlight .n { color: #f8f8f2 } /* Name */
.highlight .o { color: #f92672 } /* Operator */
.highlight .p { color: #f8f8f2 } /* Punctuation */
.highlight .ch { color: #75715e } /* Comment.Hashbang */
.highlight .cm { color: #75715e } /* Comment.Multiline */
.highlight .cp { color: #75715e } /* Comment.Preproc */
.highlight .cpf { color: #75715e } /* Comment.PreprocFile */
.highlight .c1 { color: #75715e } /* Comment.Single */
.highlight .cs { color: #75715e } /* Comment.Special */
.highlight .gd { color: #f92672 } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gi { color: #a6e22e } /* Generic.Inserted */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #75715e } /* Generic.Subheading */
.highlight .kc { color: #66d9ef } /* Keyword.Constant */
.highlight .kd { color: #66d9ef } /* Keyword.Declaration */
.highlight .kn { color: #f92672 } /* Keyword.Namespace */
.highlight .kp { color: #66d9ef } /* Keyword.Pseudo */
.highlight .kr { color: #66d9ef } /* Keyword.Reserved */
.highlight .kt { color: #66d9ef } /* Keyword.Type */
.highlight .ld { color: #e6db74 } /* Literal.Date */
.highlight .m { color: #ae81ff } /* Literal.Number */
.highlight .s { color: #e6db74 } /* Literal.String */
.highlight .na { color: #a6e22e } /* Name.Attribute */
.highlight .nb { color: #f8f8f2 } /* Name.Builtin */
.highlight .nc { color: #a6e22e } /* Name.Class */
.highlight .no { color: #66d9ef } /* Name.Constant */
.highlight .nd { color: #a6e22e } /* Name.Decorator */
.highlight .ni { color: #f8f8f2 } /* Name.Entity */
.highlight .ne { color: #a6e22e } /* Name.Exception */
.highlight .nf { color: #a6e22e } /* Name.Function */
.highlight .nl { color: #f8f8f2 } /* Name.Label */
.highlight .nn { color: #f8f8f2 } /* Name.Namespace */
.highlight .nx { color: #a6e22e } /* Name.Other */
.highlight .py { color: #f8f8f2 } /* Name.Property */
.highlight .nt { color: #f92672 } /* Name.Tag */
.highlight .nv { color: #f8f8f2 } /* Name.Variable */
.highlight .ow { color: #f92672 } /* Operator.Word */
.highlight .w { color: #f8f8f2 } /* Text.Whitespace */
.highlight .mb { color: #ae81ff } /* Literal.Number.Bin */
.highlight .mf { color: #ae81ff } /* Literal.Number.Float */
.highlight .mh { color: #ae81ff } /* Literal.Number.Hex */
.highlight .mi { color: #ae81ff } /* Literal.Number.Integer */
.highlight .mo { color: #ae81ff } /* Literal.Number.Oct */
.highlight .sa { color: #e6db74 } /* Literal.String.Affix */
.highlight .sb { color: #e6db74 } /* Literal.String.Backtick */
.highlight .sc { color: #e6db74 } /* Literal.String.Char */
.highlight .dl { color: #e6db74 } /* Literal.String.Delimiter */
.highlight .sd { color: #e6db74 } /* Literal.String.Doc */
.highlight .s2 { color: #e6db74 } /* Literal.String.Double */
.highlight .se { color: #ae81ff } /* Literal.String.Escape */
.highlight .sh { color: #e6db74 } /* Literal.String.Heredoc */
.highlight .si { color: #e6db74 } /* Literal.String.Interpol */
.highlight .sx { color: #e6db74 } /* Literal.String.Other */
.highlight .sr { color: #e6db74 } /* Literal.String.Regex */
.highlight .s1 { color: #e6db74 } /* Literal.String.Single */
.highlight .ss { color: #e6db74 } /* Literal.String.Symbol */
.highlight .bp { color: #f8f8f2 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #a6e22e } /* Name.Function.Magic */
.highlight .vc { color: #f8f8f2 } /* Name.Variable.Class */
.highlight .vg { color: #f8f8f2 } /* Name.Variable.Global */
.highlight .vi { color: #f8f8f2 } /* Name.Variable.Instance */
.highlight .vm { color: #f8f8f2 } /* Name.Variable.Magic */
.highlight .il { color: #ae81ff } /* Literal.Number.Integer.Long */
}
@media (prefers-color-scheme: light) {
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
}
/*
 * Copyright (c) 2015 Cisco and/or its affiliates.
 * 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.
 */
/* FIXME
   opcode name remove to save table space; enum
   x87
   3dnow
   cbw naming
*/

#include <vppinfra/error.h>
#include <vppinfra/byte_order.h>
#include <vppinfra/asm_x86.h>

#define foreach_x86_gp_register			\
  _  (AX)  _ (CX)  _ (DX)  _ (BX)		\
  _  (SP)  _ (BP)  _ (SI)  _ (DI)

typedef enum {
#define _(r) X86_INSN_GP_REG_##r,
  foreach_x86_gp_register
#undef _
} x86_insn_gp_register_t;

typedef union {
  struct {
    u8 rm : 3;
    u8 reg : 3;
    u8 mode : 2;
  };
  u8 byte;
} x86_insn_modrm_byte_t;

typedef union {
  struct {
    u8 base : 3;
    u8 index : 3;
    u8 log2_scale : 2;
  };
  u8 byte;
} x86_insn_sib_byte_t;

always_inline uword
x86_insn_has_modrm_byte (x86_insn_t * insn)
{
  int i;
  for (i = 0; i < ARRAY_LEN (insn->operands); i++)
    switch (insn->operands[i].code)
      {
      case 'G': case 'E': case 'M': case 'R':
	return 1;
      }
  return 0;
}

always_inline uword
x86_insn_immediate_type (x86_insn_t * insn)
{
  int i;
  for (i = 0; i < ARRAY_LEN (insn->operands); i++)
    switch (insn->operands[i].code)
      {
      case 'J':
      case 'I':
      case 'O':
	return insn->operands[i].type;
      }
  return 0;
}

/* Opcode extension in modrm byte reg field. */
#define foreach_x86_insn_modrm_reg_group		\
  _ (1) _ (1a) _ (2) _ (3) _ (4) _ (5) _ (6) _ (7)	\
  _ (8) _ (9) _ (10) _ (11) _ (12) _ (13) _ (14)	\
  _ (15) _ (16) _ (p)

#define foreach_x86_insn_sse_group				\
  _ (10) _ (28) _ (50) _ (58) _ (60) _ (68) _ (70) _ (78)	\
  _ (c0) _ (d0) _ (d8) _ (e0) _ (e8) _ (f0) _ (f8)

enum {
#define _(x) X86_INSN_MODRM_REG_GROUP_##x,
  foreach_x86_insn_modrm_reg_group
#undef _
#define _(x) X86_INSN_SSE_GROUP_##x,
  foreach_x86_insn_sse_group
#undef _
};

enum {
#define _(x)								\
  X86_INSN_FLAG_MODRM_REG_GROUP_##x					\
  = X86_INSN_FLAG_SET_MODRM_REG_GROUP (1 + X86_INSN_MODRM_REG_GROUP_##x),
  foreach_x86_insn_modrm_reg_group
#undef _

#define _(x)							\
  X86_INSN_FLAG_SSE_GROUP_##x					\
  = X86_INSN_FLAG_SET_SSE_GROUP (1 + X86_INSN_SSE_GROUP_##x),
  foreach_x86_insn_sse_group
#undef _
};

#define foreach_x86_gp_reg			\
  _ (AX) _ (CX) _ (DX) _ (BX)			\
  _ (SP) _ (BP) _ (SI) _ (DI)

#define foreach_x86_condition			\
  _ (o) _ (no)  _ (b) _ (nb)			\
  _ (z) _ (nz) _ (be) _ (nbe)			\
  _ (s) _ (ns)  _ (p) _ (np)			\
  _ (l) _ (nl) _ (le) _ (nle)

#define _3f(x,f,o0,o1,o2)			\
{						\
  .name = #x,					\
  .flags = (f),					\
  .operands[0] = { .data = #o0 },		\
  .operands[1] = { .data = #o1 },		\
  .operands[2] = { .data = #o2 },		\
}

#define _2f(x,f,o0,o1)	_3f(x,f,o0,o1,__)
#define _1f(x,f,o0)	_2f(x,f,o0,__)
#define _0f(x,f)	_1f(x,f,__)

#define _3(x,o0,o1,o2)	_3f(x,0,o0,o1,o2)
#define _2(x,o0,o1)	_2f(x,0,o0,o1)
#define _1(x,o0)	_1f(x,0,o0)
#define _0(x)		_0f(x,0)

static x86_insn_t x86_insns_one_byte[256] = {

#define _(x)					\
  _2 (x, Eb, Gb),				\
  _2 (x, Ev, Gv),				\
  _2 (x, Gb, Eb),				\
  _2 (x, Gv, Ev),				\
  _2 (x, AL, Ib),				\
  _2 (x, AX, Iz)

  /* 0x00 */
  _ (add),
  _0 (push_es),
  _0 (pop_es),
  _ (or),
  _0 (push_cs),
  _0 (escape_two_byte),

  /* 0x10 */
  _ (adc),
  _0 (push_ss),
  _0 (pop_ss),
  _ (sbb),
  _0 (push_ds),
  _0 (pop_ds),

  /* 0x20 */
  _ (and),
  _0 (segment_es),
  _0 (daa),
  _ (sub),
  _0 (segment_cs),
  _0 (das),

  /* 0x30 */
  _ (xor),
  _0 (segment_ss),
  _0 (aaa),
  _ (cmp),
  _0 (segment_ds),
  _0 (aas),

#undef _

  /* 0x40 */
#define _(r) _1 (inc, r),
  foreach_x86_gp_reg
#undef _
#define _(r) _1 (dec, r),
  foreach_x86_gp_reg
#undef _

  /* 0x50 */
#define _(r) _1f (push, X86_INSN_FLAG_DEFAULT_64_BIT, r),
  foreach_x86_gp_reg
#undef _
#define _(r) _1f (pop, X86_INSN_FLAG_DEFAULT_64_BIT, r),
  foreach_x86_gp_reg
#undef _

  /* 0x60 */
  _0 (pusha),
  _0 (popa),
  _2 (bound, Gv, Ma),
  _2 (movsxd, Gv, Ed),
  _0 (segment_fs),
  _0 (segment_gs),
  _0 (operand_type),
  _0 (address_size),
  _1f (push, X86_INSN_FLAG_DEFAULT_64_BIT, Iz),
  _3 (imul, Gv, Ev, Iz),
  _1f (push, X86_INSN_FLAG_DEFAULT_64_BIT, Ib),
  _3 (imul, Gv, Ev, Ib),
  _1 (insb, DX),
  _1 (insw, DX),
  _1 (outsb, DX),
  _1 (outsw, DX),

  /* 0x70 */
#define _(x) _1 (j##x, Jb),
  foreach_x86_condition
#undef _

  /* 0x80 */
  _2f (modrm_group_1, X86_INSN_FLAG_MODRM_REG_GROUP_1, Eb, Ib),
  _2f (modrm_group_1, X86_INSN_FLAG_MODRM_REG_GROUP_1, Ev, Iz),
  _2f (modrm_group_1, X86_INSN_FLAG_MODRM_REG_GROUP_1, Eb, Ib),
  _2f (modrm_group_1, X86_INSN_FLAG_MODRM_REG_GROUP_1, Ev, Ib),
  _2 (test, Eb, Gb),
  _2 (test, Ev, Gv),
  _2 (xchg, Eb, Gb),
  _2 (xchg, Ev, Gv),
  _2 (mov, Eb, Gb),
  _2 (mov, Ev, Gv),
  _2 (mov, Gb, Eb),
  _2 (mov, Gv, Ev),
  _2 (mov, Ev, Sw),
  _2 (lea, Gv, Ev),
  _2 (mov, Sw, Ew),
  _1f (modrm_group_1a, X86_INSN_FLAG_MODRM_REG_GROUP_1a, Ev),

  /* 0x90 */
  _0 (nop),
  _1 (xchg, CX),
  _1 (xchg, DX),
  _1 (xchg, BX),
  _1 (xchg, SP),
  _1 (xchg, BP),
  _1 (xchg, SI),
  _1 (xchg, DI),
  _0 (cbw),
  _0 (cwd),
  _1 (call, Ap),
  _0 (wait),
  _0 (pushf),
  _0 (popf),
  _0 (sahf),
  _0 (lahf),

  /* 0xa0 */
  _2 (mov, AL, Ob),
  _2 (mov, AX, Ov),
  _2 (mov, Ob, AL),
  _2 (mov, Ov, AX),
  _0 (movsb),
  _0 (movsw),
  _0 (cmpsb),
  _0 (cmpsw),
  _2 (test, AL, Ib),
  _2 (test, AX, Iz),
  _1 (stosb, AL),
  _1 (stosw, AX),
  _1 (lodsb, AL),
  _1 (lodsw, AX),
  _1 (scasb, AL),
  _1 (scasw, AX),

  /* 0xb0 */
  _2 (mov, AL, Ib),
  _2 (mov, CL, Ib),
  _2 (mov, DL, Ib),
  _2 (mov, BL, Ib),
  _2 (mov, AH, Ib),
  _2 (mov, CH, Ib),
  _2 (mov, DH, Ib),
  _2 (mov, BH, Ib),
#define _(r) _2 (mov, r, Iv),
  foreach_x86_gp_reg
#undef _

  /* 0xc0 */
  _2f (modrm_group_2, X86_INSN_FLAG_MODRM_REG_GROUP_2, Eb, Ib),
  _2f (modrm_group_2, X86_INSN_FLAG_MODRM_REG_GROUP_2, Ev, Ib),
  _1 (ret, Iw),
  _0 (ret),
  _2 (les, Gz, Mp),
  _2 (lds, Gz, Mp),
  _2f (modrm_group_11, X86_INSN_FLAG_MODRM_REG_GROUP_11, Eb, Ib),
  _2f (modrm_group_11, X86_INSN_FLAG_MODRM_REG_GROUP_11, Ev, Iz),
  _2 (enter, Iw, Ib),
  _0 (leave),
  _1 (ret, Iw),
  _0 (ret),
  _0 (int3),
  _1 (int, Ib),
  _0 (into),
  _0 (iret),

  /* 0xd0 */
  _2f (modrm_group_2, X86_INSN_FLAG_MODRM_REG_GROUP_2, Eb, 1b),
  _2f (modrm_group_2, X86_INSN_FLAG_MODRM_REG_GROUP_2, Ev, 1b),
  _2f (modrm_group_2, X86_INSN_FLAG_MODRM_REG_GROUP_2, Eb, CL),
  _2f (modrm_group_2, X86_INSN_FLAG_MODRM_REG_GROUP_2, Ev, CL),
  _0 (aam),
  _0 (aad),
  _0 (salc),
  _0 (xlat),
  /* FIXME x87 */
  _0 (bad),
  _0 (bad),
  _0 (bad),
  _0 (bad),
  _0 (bad),
  _0 (bad),
  _0 (bad),
  _0 (bad),

  /* 0xe0 */
  _1 (loopnz, Jb),
  _1 (loopz, Jb),
  _1 (loop, Jb),
  _1 (jcxz, Jb),
  _2 (in, AL, Ib),
  _2 (in, AX, Ib),
  _2 (out, Ib, AL),
  _2 (out, Ib, AX),
  _1f (call, X86_INSN_FLAG_DEFAULT_64_BIT, Jz),
  _1f ( jmp, X86_INSN_FLAG_DEFAULT_64_BIT, Jz),
  _1 (jmp, Ap),
  _1 (jmp, Jb),
  _2 (in, AL, DX),
  _2 (in, AX, DX),
  _2 (out, DX, AL),
  _2 (out, DX, AX),

  /* 0xf0 */
  _0 (lock),
  _0 (int1),
  _0 (repne),
  _0 (rep),
  _0 (hlt),
  _0 (cmc),
  _0f (modrm_group_3, X86_INSN_FLAG_MODRM_REG_GROUP_3),
  _0f (modrm_group_3, X86_INSN_FLAG_MODRM_REG_GROUP_3),
  _0 (clc),
  _0 (stc),
  _0 (cli),
  _0 (sti),
  _0 (cld),
  _0 (std),
  _1f (modrm_group_4, X86_INSN_FLAG_MODRM_REG_GROUP_4, Eb),
  _0f (modrm_group_5, X86_INSN_FLAG_MODRM_REG_GROUP_5),
};

static x86_insn_t x86_insns_two_byte[256] = {
  /* 0x00 */
  _0f (modrm_group_6, X86_INSN_FLAG_MODRM_REG_GROUP_6),
  _0f (modrm_group_7, X86_INSN_FLAG_MODRM_REG_GROUP_7),
  _2 (lar, Gv, Ew),
  _2 (lsl, Gv, Ew),
  _0 (bad),
  _0 (syscall),
  _0 (clts),
  _0 (sysret),
  _0 (invd),
  _0 (wbinvd),
  _0 (bad),
  _0 (ud2),
  _0 (bad),
  _0f (modrm_group_p, X86_INSN_FLAG_MODRM_REG_GROUP_p),
  _0 (femms),
  _0 (escape_3dnow),

  /* 0x10 */
  _2f (movups, X86_INSN_FLAG_SSE_GROUP_10, Gx, Ex),
  _2f (movups, X86_INSN_FLAG_SSE_GROUP_10, Ex, Gx),
  _2f (movlps, X86_INSN_FLAG_SSE_GROUP_10, Ex, Gx),
  _2f (movlps, X86_INSN_FLAG_SSE_GROUP_10, Gx, Ex),
  _2f (unpcklps, X86_INSN_FLAG_SSE_GROUP_10, Gx, Ex),
  _2f (unpckhps, X86_INSN_FLAG_SSE_GROUP_10, Gx, Ex),
  _2f (movhps, X86_INSN_FLAG_SSE_GROUP_10, Ex, Gx),
  _2f (movhps, X86_INSN_FLAG_SSE_GROUP_10, Gx, Ex),
  _0f (modrm_group_16, X86_INSN_FLAG_MODRM_REG_GROUP_16),
  _0 (nop),
  _0 (nop),
  _0 (nop),
  _0 (nop),
  _0 (nop),
  _0 (nop),
  _0 (nop),

  /* 0x20 */
  _2 (mov, Rv, Cv),
  _2 (mov, Rv, Dv),
  _2 (mov, Cv, Rv),
  _2 (mov, Dv, Rv),
  _0 (bad),
  _0 (bad),
  _0 (bad),
  _0 (bad),
  _2f (movaps, X86_INSN_FLAG_SSE_GROUP_28, Gx, Ex),
  _2f (movaps, X86_INSN_FLAG_SSE_GROUP_28, Ex, Gx),
  _2f (cvtpi2ps, X86_INSN_FLAG_SSE_GROUP_28, Gx, Ex),
  _2f (movntps, X86_INSN_FLAG_SSE_GROUP_28, Mx, Gx),
  _2f (cvttps2pi, X86_INSN_FLAG_SSE_GROUP_28, Gx, Ex),
  _2f (cvtps2pi, X86_INSN_FLAG_SSE_GROUP_28, Gx, Ex),
  _2f (ucomiss, X86_INSN_FLAG_SSE_GROUP_28, Gx, Ex),
  _2f (comiss, X86_INSN_FLAG_SSE_GROUP_28, Gx, Ex),

  /* 0x30 */
  _0 (wrmsr),
  _0 (rdtsc),
  _0 (rdmsr),
  _0 (rdpmc),
  _0 (sysenter),
  _0 (sysexit),
  _0 (bad),
  _0 (bad),
  _0 (bad),
  _0 (bad),
  _0 (bad),
  _0 (bad),
  _0 (bad),
  _0 (bad),
  _0 (bad),
  _0 (bad),

  /* 0x40 */
#define _(x) _2 (cmov##x, Gv, Ev),
  foreach_x86_condition
#undef _

  /* 0x50 */
  _2f (movmskps, X86_INSN_FLAG_SSE_GROUP_50, Gd, Rx),
  _2f (sqrtps, X86_INSN_FLAG_SSE_GROUP_50, Gx, Ex),
  _2f (rsqrtps, X86_INSN_FLAG_SSE_GROUP_50, Gx, Ex),
  _2f (rcpps, X86_INSN_FLAG_SSE_GROUP_50, Gx, Ex),
  _2f (andps, X86_INSN_FLAG_SSE_GROUP_50, Gx, Ex),
  _2f (andnps, X86_INSN_FLAG_SSE_GROUP_50, Gx, Ex),
  _2f (orps, X86_INSN_FLAG_SSE_GROUP_50, Gx, Ex),
  _2f (xorps, X86_INSN_FLAG_SSE_GROUP_50, Gx, Ex),
  _2f (addps, X86_INSN_FLAG_SSE_GROUP_58, Gx, Ex),
  _2f (mulps, X86_INSN_FLAG_SSE_GROUP_58, Gx, Ex),
  _2f (cvtps2pd, X86_INSN_FLAG_SSE_GROUP_58, Gx, Ex),
  _2f (cvtdq2ps, X86_INSN_FLAG_SSE_GROUP_58, Gx, Ex),
  _2f (subps, X86_INSN_FLAG_SSE_GROUP_58, Gx, Ex),
  _2f (minps, X86_INSN_FLAG_SSE_GROUP_58, Gx, Ex),
  _2f (divps, X86_INSN_FLAG_SSE_GROUP_58, Gx, Ex),
  _2f (maxps, X86_INSN_FLAG_SSE_GROUP_58, Gx, Ex),

  /* 0x60 */
  _2f (punpcklbw, X86_INSN_FLAG_SSE_GROUP_60, Gm, Em),
  _2f (punpcklwd, X86_INSN_FLAG_SSE_GROUP_60, Gm, Em),
  _2f (punpckldq, X86_INSN_FLAG_SSE_GROUP_60, Gm, Em),
  _2f (packsswb, X86_INSN_FLAG_SSE_GROUP_60, Gm, Em),
  _2f (pcmpgtb, X86_INSN_FLAG_SSE_GROUP_60, Gm, Em),
  _2f (pcmpgtw, X86_INSN_FLAG_SSE_GROUP_60, Gm, Em),
  _2f (pcmpgtd, X86_INSN_FLAG_SSE_GROUP_60, Gm, Em),
  _2f (packuswb, X86_INSN_FLAG_SSE_GROUP_60, Gm, Em),
  _2f (punpckhbw, X86_INSN_FLAG_SSE_GROUP_68, Gm, Em),
  _2f (punpckhwd, X86_INSN_FLAG_SSE_GROUP_68, Gm, Em),
  _2f (punpckhdq, X86_INSN_FLAG_SSE_GROUP_68, Gm, Em),
  _2f (packssdw, X86_INSN_FLAG_SSE_GROUP_68, Gm, Em),
  _0f (bad, X86_INSN_FLAG_SSE_GROUP_68),
  _0f (bad, X86_INSN_FLAG_SSE_GROUP_68),
  _2f (movd, X86_INSN_FLAG_SSE_GROUP_68, Gm, Em),
  _2f (movq, X86_INSN_FLAG_SSE_GROUP_68, Gm, Em),

  /* 0x70 */
  _3f (pshufw, X86_INSN_FLAG_SSE_GROUP_70, Gm, Em, Ib),
  _0f (modrm_group_12, X86_INSN_FLAG_MODRM_REG_GROUP_12),
  _0f (modrm_group_13, X86_INSN_FLAG_MODRM_REG_GROUP_13),
  _0f (modrm_group_14, X86_INSN_FLAG_MODRM_REG_GROUP_14),
  _2f (pcmpeqb, X86_INSN_FLAG_SSE_GROUP_70, Gm, Em),
  _2f (pcmpeqw, X86_INSN_FLAG_SSE_GROUP_70, Gm, Em),
  _2f (pcmpeqd, X86_INSN_FLAG_SSE_GROUP_70, Gm, Em),
  _0f (emms, X86_INSN_FLAG_SSE_GROUP_70),
  _0f (bad, X86_INSN_FLAG_SSE_GROUP_78),
  _0f (bad, X86_INSN_FLAG_SSE_GROUP_78),
  _0f (bad, X86_INSN_FLAG_SSE_GROUP_78),
  _0f (bad, X86_INSN_FLAG_SSE_GROUP_78),
  _0f (bad, X86_INSN_FLAG_SSE_GROUP_78),
  _0f (bad, X86_INSN_FLAG_SSE_GROUP_78),
  _2f (movd, X86_INSN_FLAG_SSE_GROUP_78, Em, Gm),
  _2f (movq, X86_INSN_FLAG_SSE_GROUP_78, Em, Gm),

  /* 0x80 */
#define _(x) _1 (jmp##x, Jz),
  foreach_x86_condition
#undef _

  /* 0x90 */
#define _(x) _1 (set##x, Eb),
  foreach_x86_condition
#undef _

  /* 0xa0 */
  _0 (push_fs),
  _0 (pop_fs),
  _0 (cpuid),
  _2 (bt, Ev, Gv),
  _3 (shld, Ev, Gv, Ib),
  _3 (shld, Ev, Gv, CL),
  _0 (bad),
  _0 (bad),
  _0 (push_gs),
  _0 (pop_gs),
  _0 (rsm),
  _2 (bts, Ev, Gv),
  _3 (shrd, Ev, Gv, Ib),
  _3 (shrd, Ev, Gv, CL),
  _0f (modrm_group_15, X86_INSN_FLAG_MODRM_REG_GROUP_15),
  _2 (imul, Gv, Ev),

  /* 0xb0 */
  _2 (cmpxchg, Eb, Gb),
  _2 (cmpxchg, Ev, Gv),
  _2 (lss, Gz, Mp),
  _2 (btr, Ev, Gv),
  _2 (lfs, Gz, Mp),
  _2 (lgs, Gz, Mp),
  _2 (movzbl, Gv, Eb),
  _2 (movzwl, Gv, Ew),
  _0 (bad),
  _0f (modrm_group_10, X86_INSN_FLAG_MODRM_REG_GROUP_10),
  _2f (modrm_group_8, X86_INSN_FLAG_MODRM_REG_GROUP_8, Ev, Ib),
  _2 (btc, Ev, Gv),
  _2 (bsf, Gv, Ev),
  _2 (bsr, Gv, Ev),
  _2 (movsx, Gv, Eb),
  _2 (movsx, Gv, Ew),

  /* 0xc0 */
  _2 (xadd, Eb, Gb),
  _2 (xadd, Ev, Gv),
  _3f (cmpps, X86_INSN_FLAG_SSE_GROUP_c0, Gx, Ex, Ib),
  _2 (movnti, Mv, Gv),
  _3f (pinsrw, X86_INSN_FLAG_SSE_GROUP_c0, Gm, Ew, Ib),
  _3f (pextrw, X86_INSN_FLAG_SSE_GROUP_c0, Gd, Rm, Ib),
  _3f (shufps, X86_INSN_FLAG_SSE_GROUP_c0, Gx, Ex, Ib),
  _1f (modrm_group_9, X86_INSN_FLAG_MODRM_REG_GROUP_9, Mx),
#define _(r) _1 (bswap, r),
  foreach_x86_gp_reg
#undef _

  /* 0xd0 */
  _0f (bad, X86_INSN_FLAG_SSE_GROUP_d0),
  _2f (psrlw, X86_INSN_FLAG_SSE_GROUP_d0, Gm, Em),
  _2f (psrld, X86_INSN_FLAG_SSE_GROUP_d0, Gm, Em),
  _2f (psrlq, X86_INSN_FLAG_SSE_GROUP_d0, Gm, Em),
  _2f (paddq, X86_INSN_FLAG_SSE_GROUP_d0, Gm, Em),
  _2f (pmullw, X86_INSN_FLAG_SSE_GROUP_d0, Gm, Em),
  _0f (bad, X86_INSN_FLAG_SSE_GROUP_d0),
  _2f (pmovmskb, X86_INSN_FLAG_SSE_GROUP_d0, Gd, Rm),
  _2f (psubusb, X86_INSN_FLAG_SSE_GROUP_d8, Gm, Em),
  _2f (psubusw, X86_INSN_FLAG_SSE_GROUP_d8, Gm, Em),
  _2f (pminub, X86_INSN_FLAG_SSE_GROUP_d8, Gm, Em),
  _2f (pand, X86_INSN_FLAG_SSE_GROUP_d8, Gm, Em),
  _2f (paddusb, X86_INSN_FLAG_SSE_GROUP_d8, Gm, Em),
  _2f (paddusw, X86_INSN_FLAG_SSE_GROUP_d8, Gm, Em),
  _2f (pmaxub, X86_INSN_FLAG_SSE_GROUP_d8, Gm, Em),
  _2f (pandn, X86_INSN_FLAG_SSE_GROUP_d8, Gm, Em),

  /* 0xe0 */
  _2f (pavgb, X86_INSN_FLAG_SSE_GROUP_e0, Gm, Em),
  _2f (psraw, X86_INSN_FLAG_SSE_GROUP_e0, Gm, Em),
  _2f (psrad, X86_INSN_FLAG_SSE_GROUP_e0, Gm, Em),
  _2f (pavgw, X86_INSN_FLAG_SSE_GROUP_e0, Gm, Em),
  _2f (pmulhuw, X86_INSN_FLAG_SSE_GROUP_e0, Gm, Em),
  _2f (pmulhw, X86_INSN_FLAG_SSE_GROUP_e0, Gm, Em),
  _2f (bad, X86_INSN_FLAG_SSE_GROUP_e0, Gm, Em),
  _2f (movntq, X86_INSN_FLAG_SSE_GROUP_e0, Mm, Gm),
  _2f (psubsb, X86_INSN_FLAG_SSE_GROUP_e8, Gm, Em),
  _2f (psubsw, X86_INSN_FLAG_SSE_GROUP_e8, Gm, Em),
  _2f (pminsw, X86_INSN_FLAG_SSE_GROUP_e8, Gm, Em),
  _2f (por, X86_INSN_FLAG_SSE_GROUP_e8, Gm, Em),
  _2f (paddsb, X86_INSN_FLAG_SSE_GROUP_e8, Gm, Em),
  _2f (paddsw, X86_INSN_FLAG_SSE_GROUP_e8, Gm, Em),
  _2f (pmaxsw, X86_INSN_FLAG_SSE_GROUP_e8, Gm, Em),
  _2f (pxor, X86_INSN_FLAG_SSE_GROUP_e8, Gm, Em),

  /* 0xf0 */
  _0f (bad, X86_INSN_FLAG_SSE_GROUP_f0),
  _2f (psllw, X86_INSN_FLAG_SSE_GROUP_f0, Gm, Em),
  _2f (pslld, X86_INSN_FLAG_SSE_GROUP_f0, Gm, Em),
  _2f (psllq, X86_INSN_FLAG_SSE_GROUP_f0, Gm, Em),
  _2f (pmuludq, X86_INSN_FLAG_SSE_GROUP_f0, Gm, Em),
  _2f (pmaddwd, X86_INSN_FLAG_SSE_GROUP_f0, Gm, Em),
  _2f (psadbw, X86_INSN_FLAG_SSE_GROUP_f0, Gm, Em),
  _2f (maskmovq, X86_INSN_FLAG_SSE_GROUP_f0, Gm, Em),
  _2f (psubb, X86_INSN_FLAG_SSE_GROUP_f8, Gm, Em),
  _2f (psubw, X86_INSN_FLAG_SSE_GROUP_f8, Gm, Em),
  _2f (psubd, X86_INSN_FLAG_SSE_GROUP_f8, Gm, Em),
  _2f (psubq, X86_INSN_FLAG_SSE_GROUP_f8, Gm, Em),
  _2f (paddb, X86_INSN_FLAG_SSE_GROUP_f8, Gm, Em),
  _2f (paddw, X86_INSN_FLAG_SSE_GROUP_f8, Gm, Em),
  _2f (paddd, X86_INSN_FLAG_SSE_GROUP_f8, Gm, Em),
  _0f (bad, X86_INSN_FLAG_SSE_GROUP_f8),
};

typedef struct {
  x86_insn_t insns[8];
} x86_insn_group8_t;

/* Escape groups are indexed by modrm reg field. */
static x86_insn_group8_t x86_insn_modrm_reg_groups[] = {
  [X86_INSN_MODRM_REG_GROUP_1].insns = {
    _0 (add), _0 ( or), _0 (adc), _0 (sbb),
    _0 (and), _0 (sub), _0 (xor), _0 (cmp),
  },

  [X86_INSN_MODRM_REG_GROUP_1a].insns = {
    _0f (pop, X86_INSN_FLAG_DEFAULT_64_BIT),
    _0 (bad), _0 (bad), _0 (bad),
    _0 (bad), _0 (bad), _0 (bad), _0 (bad),
  },

  [X86_INSN_MODRM_REG_GROUP_2].insns = {
    _0 (rol), _0 (ror), _0 (rcl), _0 (rcr),
    _0 (shl), _0 (shr), _0 (sal), _0 (sar),
  },

  [X86_INSN_MODRM_REG_GROUP_3].insns = {
    _0 (test), _0 (test), _0 (not), _0 (neg),
    _0 (mul), _0 (imul), _0 (div), _0 (idiv),
  },

  [X86_INSN_MODRM_REG_GROUP_4].insns = {
    _0 (inc), _0 (dec), _0 (bad), _0 (bad),
    _0 (bad), _0 (bad), _0 (bad), _0 (bad),
  },

  [X86_INSN_MODRM_REG_GROUP_5].insns = {
    _1 (inc, Ev),
    _1 (dec, Ev),
    _1f (call, X86_INSN_FLAG_DEFAULT_64_BIT, Ev),
    _1 (call, Mp),
    _1f (jmp, X86_INSN_FLAG_DEFAULT_64_BIT, Ev),
    _1 (jmp, Mp),
    _1f (push, X86_INSN_FLAG_DEFAULT_64_BIT, Ev),
    _0 (bad),
  },

  [X86_INSN_MODRM_REG_GROUP_6].insns = {
    _1 (sldt, Ev),
    _1 (str, Ev),
    _1 (lldt, Ev),
    _1 (ltr, Ev),
    _1 (verr, Ev),
    _1 (verw, Ev),
    _0 (bad),
    _0 (bad),
  },

  [X86_INSN_MODRM_REG_GROUP_7].insns = {
    _1 (sgdt, Mv),
    _1 (sidt, Mv),
    _1 (lgdt, Mv),
    _1 (lidt, Mv),
    _1 (smsw, Ev),
    _0 (bad),
    _1 (lmsw, Ew),
    _1 (invlpg, Mv),
  },

  [X86_INSN_MODRM_REG_GROUP_8].insns = {
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _2 (bt, Ev, Ib),
    _2 (bts, Ev, Ib),
    _2 (btr, Ev, Ib),
    _2 (btc, Ev, Ib),
  },

  [X86_INSN_MODRM_REG_GROUP_9].insns = {
    _0 (bad),
    _1 (cmpxchg, Mx),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
  },

  [X86_INSN_MODRM_REG_GROUP_10].insns = {
    _0 (bad), _0 (bad), _0 (bad), _0 (bad),
    _0 (bad), _0 (bad), _0 (bad), _0 (bad),
  },

  [X86_INSN_MODRM_REG_GROUP_11].insns = {
    _0 (mov), _0 (bad), _0 (bad), _0 (bad),
    _0 (bad), _0 (bad), _0 (bad), _0 (bad),
  },

  [X86_INSN_MODRM_REG_GROUP_12].insns = {
    _0 (bad),
    _0 (bad),
    _2 (psrlw, Rm, Ib),
    _0 (bad),
    _2 (psraw, Rm, Ib),
    _0 (bad),
    _2 (psllw, Rm, Ib),
    _0 (bad),
  },

  [X86_INSN_MODRM_REG_GROUP_13].insns = {
    _0 (bad),
    _0 (bad),
    _2 (psrld, Rm, Ib),
    _0 (bad),
    _2 (psrad, Rm, Ib),
    _0 (bad),
    _2 (pslld, Rm, Ib),
    _0 (bad),
  },

  [X86_INSN_MODRM_REG_GROUP_14].insns = {
    _0 (bad),
    _0 (bad),
    _2 (psrlq, Rm, Ib),
    _0f (bad, 0),
    _0 (bad),
    _0 (bad),
    _2 (psllq, Rm, Ib),
    _0f (bad, 0),
  },

  [X86_INSN_MODRM_REG_GROUP_15].insns = {
    _1 (fxsave, Mv),
    _1 (fxrstor, Mv),
    _1 (ldmxcsr, Mv),
    _1 (stmxcsr, Mv),
    _0 (bad),
    _1 (lfence, Mv),
    _1 (mfence, Mv),
    _1 (sfence, Mv),
  },

  [X86_INSN_MODRM_REG_GROUP_16].insns = {
    _1 (prefetch_nta, Mv),
    _1 (prefetch_t0, Mv),
    _1 (prefetch_t1, Mv),
    _1 (prefetch_t2, Mv),
    _1 (prefetch_nop, Mv),
    _1 (prefetch_nop, Mv),
    _1 (prefetch_nop, Mv),
    _1 (prefetch_nop, Mv),
  },

  [X86_INSN_MODRM_REG_GROUP_p].insns = {
    _1 (prefetch_exclusive, Mv),
    _1 (prefetch_modified, Mv),
    _1 (prefetch_nop, Mv),
    _1 (prefetch_modified, Mv),
    _1 (prefetch_nop, Mv),
    _1 (prefetch_nop, Mv),
    _1 (prefetch_nop, Mv),
    _1 (prefetch_nop, Mv),
  },
};

static x86_insn_group8_t x86_insn_sse_groups_repz[] = {
  [X86_INSN_SSE_GROUP_10].insns = {
    _2 (movss, Gx, Ex),
    _2 (movss, Ex, Gx),
    _2 (movsldup, Gx, Ex),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _2 (movshdup, Gx, Ex),
    _0 (bad),
  },

  [X86_INSN_SSE_GROUP_28].insns = {
    _0 (bad),
    _0 (bad),
    _2 (cvtsi2ss, Gx, Ev),
    _0 (bad),
    _2 (cvttss2si, Gv, Ex),
    _2 (cvtss2si, Gv, Ex),
    _0 (bad),
    _0 (bad),
  },

  [X86_INSN_SSE_GROUP_50].insns = {
    _0 (bad),
    _2 (sqrtss, Gx, Ex),
    _2 (rsqrtps, Gx, Ex),
    _2 (rcpss, Gx, Ex),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
  },

  [X86_INSN_SSE_GROUP_58].insns = {
    _2 (addss, Gx, Ex),
    _2 (mulss, Gx, Ex),
    _2 (cvtss2sd, Gx, Ex),
    _2 (cvttps2dq, Gx, Ex),
    _2 (subss, Gx, Ex),
    _2 (minss, Gx, Ex),
    _2 (divss, Gx, Ex),
    _2 (maxss, Gx, Ex),
  },

  [X86_INSN_SSE_GROUP_60].insns = {
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
  },

  [X86_INSN_SSE_GROUP_68].insns = {
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _2 (movdqu, Gx, Ex),
  },

  [X86_INSN_SSE_GROUP_70].insns = {
    _3 (pshufhw, Gx, Ex, Ib),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
  },

  [X86_INSN_SSE_GROUP_78].insns = {
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _2 (movq, Gx, Ex),
    _2 (movdqu, Ex, Gx),
  },

  [X86_INSN_SSE_GROUP_c0].insns = {
    _0 (bad),
    _0 (bad),
    _3 (cmpss, Gx, Ex, Ib),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
  },

  [X86_INSN_SSE_GROUP_d0].insns = {
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _2 (movq2dq, Gx, Em),
    _0 (bad),
  },

  [X86_INSN_SSE_GROUP_d8].insns = {
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
  },

  [X86_INSN_SSE_GROUP_e0].insns = {
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _2 (cvtdq2pd, Gx, Ex),
    _0 (bad),
  },

  [X86_INSN_SSE_GROUP_e8].insns = {
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
  },

  [X86_INSN_SSE_GROUP_f0].insns = {
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
  },

  [X86_INSN_SSE_GROUP_f8].insns = {
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
  },
};

static x86_insn_group8_t x86_insn_sse_groups_operand_size[] = {
  [X86_INSN_SSE_GROUP_10].insns = {
    _2 (movupd, Gx, Ex),
    _2 (movupd, Ex, Gx),
    _2 (movlpd, Gx, Ex),
    _2 (movlpd, Ex, Gx),
    _2 (unpcklpd, Gx, Ex),
    _2 (unpckhpd, Gx, Ex),
    _2 (movhpd, Gx, Mx),
    _2 (movhpd, Mx, Gx),
  },

  [X86_INSN_SSE_GROUP_28].insns = {
    _2 (movapd, Gx, Ex),
    _2 (movapd, Ex, Gx),
    _2 (cvtpi2pd, Gx, Ex),
    _2 (movntpd, Mx, Gx),
    _2 (cvttpd2pi, Gx, Mx),
    _2 (cvtpd2pi, Gx, Mx),
    _2 (ucomisd, Gx, Ex),
    _2 (comisd, Gx, Ex),
  },

  [X86_INSN_SSE_GROUP_50].insns = {
    _2 (movmskpd, Gd, Rx),
    _2 (sqrtpd, Gx, Ex),
    _0 (bad),
    _0 (bad),
    _2 (andpd, Gx, Ex),
    _2 (andnpd, Gx, Ex),
    _2 (orpd, Gx, Ex),
    _2 (xorpd, Gx, Ex),
  },

  [X86_INSN_SSE_GROUP_58].insns = {
    _2 (addpd, Gx, Ex),
    _2 (mulpd, Gx, Ex),
    _2 (cvtpd2ps, Gx, Ex),
    _2 (cvtps2dq, Gx, Ex),
    _2 (subpd, Gx, Ex),
    _2 (minpd, Gx, Ex),
    _2 (divpd, Gx, Ex),
    _2 (maxpd, Gx, Ex),
  },

  [X86_INSN_SSE_GROUP_60].insns = {
    _2 (punpcklbw, Gx, Ex),
    _2 (punpcklwd, Gx, Ex),
    _2 (punpckldq, Gx, Ex),
    _2 (packsswb, Gx, Ex),
    _2 (pcmpgtb, Gx, Ex),
    _2 (pcmpgtw, Gx, Ex),
    _2 (pcmpgtd, Gx, Ex),
    _2 (packuswb, Gx, Ex),
  },

  [X86_INSN_SSE_GROUP_68].insns = {
    _2 (punpckhbw, Gx, Ex),
    _2 (punpckhwd, Gx, Ex),
    _2 (punpckhdq, Gx, Ex),
    _2 (packssdw, Gx, Ex),
    _2 (punpcklqdq, Gx, Ex),
    _2 (punpckhqdq, Gx, Ex),
    _2 (movd, Gx, Ev),
    _2 (movdqa, Gx, Ex),
  },

  [X86_INSN_SSE_GROUP_70].insns = {
    _3 (pshufd, Gx, Ex, Ib),
    _0f (modrm_group_12, X86_INSN_FLAG_MODRM_REG_GROUP_12),
    _0f (modrm_group_13, X86_INSN_FLAG_MODRM_REG_GROUP_13),
    _0f (modrm_group_14, X86_INSN_FLAG_MODRM_REG_GROUP_14),
    _2 (pcmpeqb, Gx, Ex),
    _2 (pcmpeqw, Gx, Ex),
    _2 (pcmpeqd, Gx, Ex),
    _0 (bad),
  },

  [X86_INSN_SSE_GROUP_78].insns = {
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _2 (haddpd, Gx, Ex),
    _2 (hsubpd, Gx, Ex),
    _2 (movd, Ev, Gx),
    _2 (movdqa, Ex, Gx),
  },

  [X86_INSN_SSE_GROUP_c0].insns = {
    _0 (bad),
    _0 (bad),
    _3 (cmppd, Gx, Ex, Ib),
    _0 (bad),
    _3 (pinsrw, Gx, Ew, Ib),
    _3 (pextrw, Gd, Gx, Ib),
    _3 (shufpd, Gx, Ex, Ib),
    _0 (bad),
  },

  [X86_INSN_SSE_GROUP_d0].insns = {
    _2 (addsubpd, Gx, Ex),
    _2 (psrlw, Gx, Ex),
    _2 (psrld, Gx, Ex),
    _2 (psrlq, Gx, Ex),
    _2 (paddq, Gx, Ex),
    _2 (pmullw, Gx, Ex),
    _2 (movq, Ex, Gx),
    _2 (pmovmskb, Gd, Rx),
  },

  [X86_INSN_SSE_GROUP_d8].insns = {
    _2 (psubusb, Gx, Ex),
    _2 (psubusw, Gx, Ex),
    _2 (pminub, Gx, Ex),
    _2 (pand, Gx, Ex),
    _2 (paddusb, Gx, Ex),
    _2 (paddusw, Gx, Ex),
    _2 (pmaxub, Gx, Ex),
    _2 (pandn, Gx, Ex),
  },

  [X86_INSN_SSE_GROUP_e0].insns = {
    _2 (pavgb, Gx, Ex),
    _2 (psraw, Gx, Ex),
    _2 (psrad, Gx, Ex),
    _2 (pavgw, Gx, Ex),
    _2 (pmulhuw, Gx, Ex),
    _2 (pmulhw, Gx, Ex),
    _2 (cvttpd2dq, Gx, Ex),
    _2 (movntdq, Mx, Gx),
  },

  [X86_INSN_SSE_GROUP_e8].insns = {
    _2 (psubsb, Gx, Ex),
    _2 (psubsw, Gx, Ex),
    _2 (pminsw, Gx, Ex),
    _2 (por, Gx, Ex),
    _2 (paddsb, Gx, Ex),
    _2 (paddsw, Gx, Ex),
    _2 (pmaxsw, Gx, Ex),
    _2 (pxor, Gx, Ex),
  },

  [X86_INSN_SSE_GROUP_f0].insns = {
    _0 (bad),
    _2 (psllw, Gx, Ex),
    _2 (pslld, Gx, Ex),
    _2 (psllq, Gx, Ex),
    _2 (pmuludq, Gx, Ex),
    _2 (pmaddwd, Gx, Ex),
    _2 (psadbw, Gx, Ex),
    _2 (maskmovdqu, Gx, Ex),
  },

  [X86_INSN_SSE_GROUP_f8].insns = {
    _2 (psubb, Gx, Ex),
    _2 (psubw, Gx, Ex),
    _2 (psubd, Gx, Ex),
    _2 (psubq, Gx, Ex),
    _2 (paddb, Gx, Ex),
    _2 (paddw, Gx, Ex),
    _2 (paddd, Gx, Ex),
    _0 (bad),
  },
};

static x86_insn_group8_t x86_insn_sse_groups_repnz[] = {
  [X86_INSN_SSE_GROUP_10].insns = {
    _2 (movsd, Gx, Ex),
    _2 (movsd, Ex, Gx),
    _2 (movddup, Gx, Ex),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
  },

  [X86_INSN_SSE_GROUP_28].insns = {
    _0 (bad),
    _0 (bad),
    _2 (cvtsi2sd, Gx, Ev),
    _0 (bad),
    _2 (cvttsd2si, Gv, Ex),
    _2 (cvtsd2si, Gv, Ex),
    _0 (bad),
    _0 (bad),
  },

  [X86_INSN_SSE_GROUP_50].insns = {
    _0 (bad),
    _2 (sqrtsd, Gx, Ex),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
  },

  [X86_INSN_SSE_GROUP_58].insns = {
    _2 (addsd, Gx, Ex),
    _2 (mulsd, Gx, Ex),
    _2 (cvtsd2ss, Gx, Ex),
    _0 (bad),
    _2 (subsd, Gx, Ex),
    _2 (minsd, Gx, Ex),
    _2 (divsd, Gx, Ex),
    _2 (maxsd, Gx, Ex),
  },

  [X86_INSN_SSE_GROUP_60].insns = {
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
  },

  [X86_INSN_SSE_GROUP_68].insns = {
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
  },

  [X86_INSN_SSE_GROUP_70].insns = {
    _3 (pshuflw, Gx, Ex, Ib),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
  },

  [X86_INSN_SSE_GROUP_78].insns = {
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _2 (haddps, Gx, Ex),
    _2 (hsubps, Gx, Ex),
    _0 (bad),
    _0 (bad),
  },

  [X86_INSN_SSE_GROUP_c0].insns = {
    _0 (bad),
    _0 (bad),
    _3 (cmpsd, Gx, Ex, Ib),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
  },

  [X86_INSN_SSE_GROUP_d0].insns = {
    _2 (addsubps, Gx, Ex),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _2 (movdq2q, Gm, Ex),
    _0 (bad),
  },

  [X86_INSN_SSE_GROUP_d8].insns = {
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
  },

  [X86_INSN_SSE_GROUP_e0].insns = {
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _2 (cvtpd2dq, Gx, Ex),
    _0 (bad),
  },

  [X86_INSN_SSE_GROUP_e8].insns = {
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
  },

  [X86_INSN_SSE_GROUP_f0].insns = {
    _2 (lddqu, Gx, Mx),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
  },

  [X86_INSN_SSE_GROUP_f8].insns = {
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
    _0 (bad),
  },
};

#undef _

/* Parses memory displacements and immediates. */
static u8 * x86_insn_parse_number (u32 log2_n_bytes,
				   u8 * code, u8 * code_end,
				   i64 * result)
{
  i64 x = 0;

  if (code + (1 << log2_n_bytes) > code_end)
    return 0;

  switch (log2_n_bytes)
    {
    case 3:
      x = clib_little_to_host_unaligned_mem_u64 ((u64 *) code);
      break;

    case 2:
      x = (i32) clib_little_to_host_unaligned_mem_u32 ((u32 *) code);
      break;

    case 1:
      x = (i16) clib_little_to_host_unaligned_mem_u16 ((u16 *) code);
      break;

    case 0:
      x = (i8) code[0];
      break;

    default:
      ASSERT (0);
    }

  *result = x;
  return code + (1 << log2_n_bytes);
}

static u32
x86_insn_log2_immediate_bytes (x86_insn_parse_t * p, x86_insn_t * insn)
{
  u32 i = ~0;
  switch (x86_insn_immediate_type (insn))
    {
    case 'b': i = 0; break;
    case 'w': i = 1; break;
    case 'd': i = 2; break;
    case 'q': i = 3; break;

    case 'z':
      i = p->log2_effective_operand_bytes;
      if (i > 2) i = 2;
      break;

    case 'v':
      i = p->log2_effective_operand_bytes;
      break;

    default:
      i = ~0;
      break;
    }

  return i;
}

static u8 *
x86_insn_parse_modrm_byte (x86_insn_parse_t * x,
			   x86_insn_modrm_byte_t modrm,
			   u32 parse_flags,
			   u8 * code,
			   u8 * code_end)
{
  u8 effective_address_bits;

  if (parse_flags & X86_INSN_PARSE_64_BIT)
    effective_address_bits = (x->flags & X86_INSN_ADDRESS_SIZE) ? 32 : 64;
  else if (parse_flags & X86_INSN_PARSE_32_BIT)
    effective_address_bits = (x->flags & X86_INSN_ADDRESS_SIZE) ? 16 : 32;
  else
    effective_address_bits = (x->flags & X86_INSN_ADDRESS_SIZE) ? 32 : 16;

  x->log2_effective_address_bytes = 1;
  x->log2_effective_address_bytes += effective_address_bits > 16;
  x->log2_effective_address_bytes += effective_address_bits > 32;

  x->regs[0] |= modrm.reg;
  if (modrm.mode == 3)
    x->regs[1] |= modrm.rm;
  else
    {
      u32 log2_disp_bytes = ~0;

      x->flags |= X86_INSN_IS_ADDRESS;

      if (effective_address_bits != 16)
	{
	  u8 has_sib_byte = 0;

	  switch (modrm.mode)
	    {
	    case 0:
	      /* When base is bp displacement is present for mode 0. */
	      if (modrm.rm == X86_INSN_GP_REG_BP)
		{
		  log2_disp_bytes = x->log2_effective_address_bytes;
		  break;
		}
	      else if (modrm.rm == X86_INSN_GP_REG_SP
		       && effective_address_bits != 16)
		{
		  has_sib_byte = 1;
		  break;
		}
	      /* fall through */
	    case 1:
	    case 2:
	      x->regs[1] |= modrm.rm;
	      x->flags |= X86_INSN_HAS_BASE;
	      if (modrm.mode != 0)
		{
		  log2_disp_bytes = (modrm.mode == 1
				     ? 0
				     : x->log2_effective_address_bytes);
		  if (log2_disp_bytes > 2)
		    log2_disp_bytes = 2;
		}
	      break;
	    }

	  if (has_sib_byte)
	    {
	      x86_insn_sib_byte_t sib;

	      if (code >= code_end)
		return 0;
	      sib.byte = *code++;

	      x->log2_index_scale = 1 << sib.log2_scale;
	      x->regs[1] |= sib.base;
	      x->flags |= X86_INSN_HAS_BASE;

	      if (sib.index != X86_INSN_GP_REG_SP)
		{
		  x->regs[2] |= sib.index;
		  x->flags |= X86_INSN_HAS_INDEX;
		}
	    }
	}
      else
	{
	  /* effective_address_bits == 16 */
	  switch (modrm.mode)
	    {
	    case 0:
	      if (modrm.rm == 6)
		{
		  /* [disp16] */
		  log2_disp_bytes = 1;
		  break;
		}
	      /* fall through */
	    case 1:
	    case 2:
	      switch (modrm.rm)
		{
		case 0:		/* [bx + si/di] */
		case 1:
		  x->regs[1] = X86_INSN_GP_REG_BX;
		  x->regs[2] = X86_INSN_GP_REG_SI + (modrm.rm & 1);
		  x->flags |= X86_INSN_HAS_BASE | X86_INSN_HAS_INDEX;
		  break;

		case 2:		/* [bp + si/di] */
		case 3:
		  x->regs[1] = X86_INSN_GP_REG_BP;
		  x->regs[2] = X86_INSN_GP_REG_SI + (modrm.rm & 1);
		  x->flags |= X86_INSN_HAS_BASE | X86_INSN_HAS_INDEX;
		  break;

		case 4:		/* [si/di] */
		case 5:
		  x->regs[1] = X86_INSN_GP_REG_SI + (modrm.rm & 1);
		  x->flags |= X86_INSN_HAS_BASE;
		  break;

		case 6:		/* [bp + disp] */
		  x->regs[1] = X86_INSN_GP_REG_BP;
		  x->flags |= X86_INSN_HAS_BASE;
		  break;

		case 7:		/* [bx + disp] */
		  x->regs[1] = X86_INSN_GP_REG_BX;
		  x->flags |= X86_INSN_HAS_BASE;
		  break;
		}

	      if (modrm.mode != 0)
		log2_disp_bytes = modrm.mode == 1 ? 0 : 1;
	      break;
	    }
	}
      
      if (log2_disp_bytes != ~0)
	{
	  i64 disp;
	  code = x86_insn_parse_number (log2_disp_bytes, code, code_end,
					&disp);
	  if (code)
	    x->displacement = disp;
	}
    }

  return code;
}

u8 * x86_insn_parse (x86_insn_parse_t * p, u8 * code_start)
{
  u8 i, * code, * code_end;
  x86_insn_t * insn, * group_insn;
  u8 default_operand_bits, effective_operand_bits;
  u32 opcode, parse_flags;

  /* Preserve global parse flags. */
  parse_flags = p->flags & (X86_INSN_PARSE_32_BIT | X86_INSN_PARSE_64_BIT);
  clib_memset (p, 0, sizeof (p[0]));
  p->flags = parse_flags;

  /* 64 implies 32 bit parsing. */
  if (parse_flags & X86_INSN_PARSE_64_BIT)
    parse_flags |= X86_INSN_PARSE_32_BIT;

  /* Instruction must be <= 15 bytes. */
  code = code_start;
  code_end = code + 15;

  /* Parse legacy prefixes. */
  while (1)
    {
      if (code >= code_end)
	goto insn_too_long;
      i = code[0];
      code++;
      switch (i)
	{
	default: goto prefix_done;

	  /* Set flags based on prefix. */
#define _(x,o) case o: p->flags |= X86_INSN_##x; break;
	  foreach_x86_legacy_prefix;
#undef _
	}
    }
 prefix_done:

  /* REX prefix. */
  if ((parse_flags & X86_INSN_PARSE_64_BIT) && i >= 0x40 && i <= 0x4f)
    {
      p->regs[0] |= ((i & (1 << 2)) != 0) << 3;	/* r bit */
      p->regs[1] |= ((i & (1 << 0)) != 0) << 3;	/* b bit */
      p->regs[2] |= ((i & (1 << 1)) != 0) << 3;	/* x bit */
      p->flags |= ((i & (1 << 3))		/* w bit */
		   ? X86_INSN_OPERAND_SIZE_64 : 0);
      if (code >= code_end)
	goto insn_too_long;
      i = *code++;
    }

  opcode = i;
  if (opcode == 0x0f)
    {
      /* two byte opcode. */;
      if (code >= code_end)
	goto insn_too_long;
      i = *code++;
      opcode = (opcode << 8) | i;
      insn = x86_insns_two_byte + i;
    }
  else
    {
      static x86_insn_t arpl = {
	.name = "arpl",
	.operands[0].data = "Ew",
	.operands[1].data = "Gw",
      };

      if (PREDICT_FALSE (i == 0x63
			 && ! (parse_flags & X86_INSN_PARSE_64_BIT)))
	insn = &arpl;
      else
	insn = x86_insns_one_byte + i;
    }

  if ((i = X86_INSN_FLAG_GET_SSE_GROUP (insn->flags)) != 0)
    {
      x86_insn_group8_t * g8;

      if (p->flags & X86_INSN_OPERAND_SIZE)
	g8 = x86_insn_sse_groups_operand_size;
      else if (p->flags & X86_INSN_REPZ)
	g8 = x86_insn_sse_groups_repz;
      else if (p->flags & X86_INSN_REPNZ)
	g8 = x86_insn_sse_groups_repnz;
      else
	g8 = 0;

      /* insn flags have 1 + group so != 0 test above can work. */
      ASSERT ((i - 1) < ARRAY_LEN (x86_insn_sse_groups_operand_size));
      if (g8)
	insn = g8[i - 1].insns + (opcode & 7);
    }

  /* Parse modrm and displacement if present. */
  if (x86_insn_has_modrm_byte (insn))
    {
      x86_insn_modrm_byte_t modrm;

      if (code >= code_end)
	goto insn_too_long;
      modrm.byte = *code++;

      /* Handle special 0x0f01 and 0x0fae encodings. */
      if (PREDICT_FALSE (modrm.mode == 3
			 && (opcode == 0x0f01
			     || opcode == 0x0fae)))
	{
	  static x86_insn_t x86_insns_0f01_special[] = {
	    _0 (swapgs), _0 (rdtscp), _0 (bad), _0 (bad),
	    _0 (bad), _0 (bad), _0 (bad), _0 (bad),
	  };
	  static x86_insn_t x86_insns_0fae_special[] = {
	    _0 (vmrun), _0 (vmmcall), _0 (vmload), _0 (vmsave),
	    _0 (stgi), _0 (clgi), _0 (skinit), _0 (invlpga),
	  };

	  if (opcode == 0x0f01)
	    insn = x86_insns_0f01_special;
	  else
	    insn = x86_insns_0fae_special;
	  insn += modrm.rm;
	  opcode = (opcode << 8) | modrm.byte;
	}
      else
	{
	  code = x86_insn_parse_modrm_byte (p, modrm, parse_flags,
					    code, code_end);
	  if (! code)
	    goto insn_too_long;
	}
    }

  group_insn = 0;
  if ((i = X86_INSN_FLAG_GET_MODRM_REG_GROUP (insn->flags)) != 0)
    {
      u32 g = i - 1;
      ASSERT (g < ARRAY_LEN (x86_insn_modrm_reg_groups));
      group_insn = x86_insn_modrm_reg_groups[g].insns + (p->regs[0] & 7);
    }

  p->insn = insn[0];
  if (group_insn)
    {
      u32 k;
      p->insn.name = group_insn->name;
      p->insn.flags |= group_insn->flags;
      for (k = 0; k < ARRAY_LEN (group_insn->operands); k++)
	if (x86_insn_operand_is_valid (group_insn, k))
	  p->insn.operands[k] = group_insn->operands[k];
    }

  default_operand_bits
    = ((((parse_flags & X86_INSN_PARSE_32_BIT) != 0)
	^ ((p->flags & X86_INSN_OPERAND_SIZE) != 0))
       ? BITS (u32) : BITS (u16));

  if ((parse_flags & X86_INSN_PARSE_64_BIT)
      && (p->insn.flags & X86_INSN_FLAG_DEFAULT_64_BIT))
    default_operand_bits = BITS (u64);

  effective_operand_bits = default_operand_bits;
  if (p->flags & X86_INSN_OPERAND_SIZE_64)
    effective_operand_bits = BITS (u64);

  p->log2_effective_operand_bytes = 1;
  p->log2_effective_operand_bytes += effective_operand_bits > 16;
  p->log2_effective_operand_bytes += effective_operand_bits > 32;

  /* Parse immediate if present. */
  {
    u32 l = x86_insn_log2_immediate_bytes (p, insn);
    if (l <= 3)
      {
	code = x86_insn_parse_number (l, code, code_end, &p->immediate);
	if (! code)
	  goto insn_too_long;
      }
  }

  return code;

 insn_too_long:
  return 0;
}

static u8 * format_x86_gp_reg_operand (u8 * s, va_list * va)
{
  u32 r = va_arg (*va, u32);
  u32 log2_n_bytes = va_arg (*va, u32);

  const char names8[8] = "acdbsbsd";
  const char names16[8] = "xxxxppii";

  ASSERT (r < 16);

  /* Add % register prefix. */
  vec_add1 (s, '%');

  switch (log2_n_bytes)
    {
    case 0:
      {

	if (r < 8)
	  s = format (s, "%c%c", names8[r & 3], (r >> 2) ? 'l' : 'h');
	else
	  s = format (s, "r%db", r);
      }
      break;
      
      case 2:
      case 3:
	s = format (s, "%c", log2_n_bytes == 2 ? 'e' : 'r');
	/* fall through */
      case 1:
	if (r < 8)
	  s = format (s, "%c%c", names8[r], names16[r]);
	else
	  {
	    s = format (s, "%d", r);
	    if (log2_n_bytes != 3)
	      s = format (s, "%c", log2_n_bytes == 1 ? 'w' : 'd');
	  }
	break;

    default:
      ASSERT (0);
    }

  return s;
}

static u8 * format_x86_reg_operand (u8 * s, va_list * va)
{
  u32 reg = va_arg (*va, u32);
  u32 log2_n_bytes = va_arg (*va, u32);
  u32 type = va_arg (*va, u32);

  switch (type)
    {
    default:
      ASSERT (0);
      break;

    case 'x':
      ASSERT (reg < 16);
      return format (s, "%%xmm%d", reg);

    case 'm':
      ASSERT (reg < 8);
      return format (s, "%%mm%d", reg);

      /* Explicit byte/word/double-word/quad-word */
    case 'b': log2_n_bytes = 0; break;
    case 'w': log2_n_bytes = 1; break;
    case 'd': log2_n_bytes = 2; break;
    case 'q': log2_n_bytes = 3; break;

      /* Use effective operand size. */
    case 'v': break;

      /* word or double-word depending on effective operand size. */
    case 'z':
      log2_n_bytes = clib_min (log2_n_bytes, 2);
      break;
    }

  s = format (s, "%U", format_x86_gp_reg_operand, reg, log2_n_bytes);
  return s;
}

static u8 * format_x86_mem_operand (u8 * s, va_list * va)
{
  x86_insn_parse_t * p = va_arg (*va, x86_insn_parse_t *);

  if (p->displacement != 0)
    s = format (s, "0x%x", p->displacement);

  if (p->flags & X86_INSN_HAS_BASE)
    {
      s = format (s, "(%U",
		  format_x86_gp_reg_operand, p->regs[1],
		    p->log2_effective_address_bytes);
      if (p->flags & X86_INSN_HAS_INDEX)
	{
	  s = format (s, ",%U",
		      format_x86_gp_reg_operand, p->regs[2],
		        p->log2_effective_address_bytes);
	  if (p->log2_index_scale != 0)
	    s = format (s, ",%d", 1 << p->log2_index_scale);
	}
      s = format (s, ")");
    }

  /* [RIP+disp] PC relative addressing in 64 bit mode. */
  else if (p->flags & X86_INSN_PARSE_64_BIT)
    s = format (s, "(%%rip)");

  return s;
}

static u8 * format_x86_insn_operand (u8 * s, va_list * va)
{
  x86_insn_parse_t * p = va_arg (*va, x86_insn_parse_t *);
  x86_insn_t * insn = &p->insn;
  u32 o = va_arg (*va, u32);
  u8 c, t;

  ASSERT (o < ARRAY_LEN (insn->operands));
  c = insn->operands[o].code;
  t = insn->operands[o].type;

  /* Register encoded in instruction. */
  if (c < 8)
    return format (s, "%U",
		   format_x86_gp_reg_operand, c,
		   p->log2_effective_operand_bytes);

  switch (c)
    {
    /* Memory or reg field from modrm byte. */
    case 'M':
      ASSERT (p->flags & X86_INSN_IS_ADDRESS);
      /* FALLTHROUGH */
    case 'E':
      if (p->flags & X86_INSN_IS_ADDRESS)
	s = format (s, "%U", format_x86_mem_operand, p);
      else
	s = format (s, "%U",
		    format_x86_reg_operand, p->regs[1],
		    p->log2_effective_operand_bytes, t);
      break;

    /* reg field from modrm byte. */
    case 'R':
    case 'G':
      s = format (s, "%U",
		  format_x86_reg_operand, p->regs[0],
		  p->log2_effective_operand_bytes, t);
      break;

    case 'I':
      {
	u32 l = x86_insn_log2_immediate_bytes (p, insn);
	i64 mask = pow2_mask (8ULL << l);
	s = format (s, "$0x%Lx", p->immediate & mask);
      }
      break;

    case 'J':
      if (p->immediate < 0)
	s = format (s, "- 0x%Lx", -p->immediate);
      else
	s = format (s, "+ 0x%Lx", p->immediate);
      break;

    case 'O':
      s = format (s, "0x%Lx", p->immediate);
      break;

    case 'A':
      /* AX/AL */
      s = format (s, "%U",
		  format_x86_gp_reg_operand, X86_INSN_GP_REG_AX,
		  t == 'L' ? 0 : p->log2_effective_operand_bytes);
      break;

    case 'B':
      /* BX/BL/BP */
      s = format (s, "%U",
		  format_x86_gp_reg_operand,
		  t == 'P' ? X86_INSN_GP_REG_BP : X86_INSN_GP_REG_BX,
		  t == 'L' ? 0 : p->log2_effective_operand_bytes);
      break;

    case 'C':
      /* CX/CL */
      s = format (s, "%U",
		  format_x86_gp_reg_operand, X86_INSN_GP_REG_CX,
		  t == 'L' ? 0 : p->log2_effective_operand_bytes);
      break;

    case 'D':
      /* DX/DL/DI */
      s = format (s, "%U",
		  format_x86_gp_reg_operand,
		  t == 'I' ? X86_INSN_GP_REG_DI : X86_INSN_GP_REG_DX,
		  t == 'L' ? 0 : p->log2_effective_operand_bytes);
      break;

    case 'S':
      /* SI/SP */
      s = format (s, "%U",
		  format_x86_gp_reg_operand,
		  t == 'I' ? X86_INSN_GP_REG_SI : X86_INSN_GP_REG_SP,
		  p->log2_effective_operand_bytes);
      break;

    case '1':
      s = format (s, "1");
      break;

    default:
      ASSERT (0);
    }

  return s;
}

u8 * format_x86_insn_parse (u8 * s, va_list * va)
{
  x86_insn_parse_t * p = va_arg (*va, x86_insn_parse_t *);
  x86_insn_t * insn = &p->insn;
  u32 o, i, is_src_dst;

  s = format (s, "%s", insn->name);

  if (! x86_insn_operand_is_valid (insn, 0))
    goto done;

  is_src_dst = x86_insn_operand_is_valid (insn, 1);

  /* If instruction has immediate add suffix to opcode to
     indicate operand size. */
  if (is_src_dst)
    {
      u32 b;

      b = x86_insn_log2_immediate_bytes (p, insn);
      if (b < p->log2_effective_operand_bytes
	  && (p->flags & X86_INSN_IS_ADDRESS))
	s = format (s, "%c", "bwlq"[b]);
    }

  for (i = 0; i < ARRAY_LEN (insn->operands); i++)
    {
      o = is_src_dst + i;
      if (! x86_insn_operand_is_valid (insn, o))
	break;
      s = format (s, "%s%U",
		  i == 0 ? " " : ", ",
		  format_x86_insn_operand, p, o);
    }

  if (is_src_dst)
    s = format (s, ", %U",
		format_x86_insn_operand, p, 0);

 done:
  return s;
}