odin-blend2d

Odin bindings to Blend2D
Log | Files | Refs | README | LICENSE

aarch32.js (27792B)


      1 // This file is part of AsmJit project <https://asmjit.com>
      2 //
      3 // See <asmjit/core.h> or LICENSE.md for license and copyright information
      4 // SPDX-License-Identifier: Zlib
      5 
      6 (function($scope, $as) {
      7 "use strict";
      8 
      9 function FAIL(msg) { throw new Error("[AArch32] " + msg); }
     10 
     11 // Import
     12 // ======
     13 
     14 const base = $scope.base ? $scope.base : require("./base.js");
     15 const exp = $scope.exp ? $scope.exp : require("./exp.js")
     16 
     17 const dict = base.dict;
     18 const NONE = base.NONE;
     19 const Parsing = base.Parsing;
     20 const MapUtils = base.MapUtils;
     21 
     22 // Export
     23 // ======
     24 
     25 const arm = $scope[$as] = dict();
     26 
     27 // Database
     28 // ========
     29 
     30 arm.dbName = "isa_aarch32.json";
     31 
     32 // asmdb.aarch32.Utils
     33 // ===================
     34 
     35 // Can be used to assign the number of bits each part of the opcode occupies.
     36 // NOTE: THUMB instructions that use halfword must always specify the width
     37 // of all registers as many instructions accept only LO (r0..r7) registers.
     38 const FieldInfo = {
     39   "P"     : { "bits": 1 },
     40   "U"     : { "bits": 1 },
     41   "W"     : { "bits": 1 },
     42   "S"     : { "bits": 1 },
     43   "R"     : { "bits": 1 },
     44   "H"     : { "bits": 1 },
     45   "isFp32": { "bits": 1 },
     46   "F"     : { "bits": 1 },
     47   "align" : { "bits": 2 },
     48   "ja"    : { "bits": 1 },
     49   "jb"    : { "bits": 1 },
     50   "op"    : { "bits": 1 }, // TODO: This should be fixed.
     51   "sz"    : { "bits": 2 },
     52   "sop"   : { "bits": 2 },
     53   "cond"  : { "bits": 4 },
     54   "cmode" : { "bits": 4 },
     55   "Cn"    : { "bits": 4 },
     56   "Cm"    : { "bits": 4 },
     57 
     58   "Rd"    : { "bits": 4, "read": false, "write": true  },
     59   "Rd2"   : { "bits": 4, "read": false, "write": true  },
     60   "RdLo"  : { "bits": 4, "read": false, "write": true  },
     61   "RdHi"  : { "bits": 4, "read": false, "write": true  },
     62   "RdList": { "bits": 4, "read": false, "write": true  , "list": true },
     63   "Rx"    : { "bits": 4, "read": true , "write": true  },
     64   "RxLo"  : { "bits": 4, "read": true , "write": true  },
     65   "RxHi"  : { "bits": 4, "read": true , "write": true  },
     66   "Rn"    : { "bits": 4, "read": true , "write": false },
     67   "Rm"    : { "bits": 4, "read": true , "write": false },
     68   "Ra"    : { "bits": 4, "read": true , "write": false },
     69   "Rs"    : { "bits": 4, "read": true , "write": false },
     70   "Rs2"   : { "bits": 4, "read": true , "write": false },
     71   "RsList": { "bits": 4, "read": true , "write": false , "list": true },
     72 
     73   "Sd"    : { "bits": 4, "read": false, "write": true  },
     74   "Sd2"   : { "bits": 4, "read": false, "write": true  },
     75   "SdList": { "bits": 4, "read": false, "write": true  , "list": true },
     76   "Sx"    : { "bits": 4, "read": true , "write": true  },
     77   "Sn"    : { "bits": 4, "read": true , "write": false },
     78   "Sm"    : { "bits": 4, "read": true , "write": false },
     79   "Ss"    : { "bits": 4, "read": true , "write": false },
     80   "Ss2"   : { "bits": 4, "read": true , "write": false },
     81   "SsList": { "bits": 4, "read": true , "write": false , "list": true },
     82 
     83   "Dd"    : { "bits": 4, "read": false, "write": true  },
     84   "Dd2"   : { "bits": 4, "read": false, "write": true  },
     85   "Dd3"   : { "bits": 4, "read": false, "write": true  },
     86   "Dd4"   : { "bits": 4, "read": false, "write": true  },
     87   "DdList": { "bits": 4, "read": false, "write": true  , "list": true },
     88   "Dx"    : { "bits": 4, "read": true , "write": true  },
     89   "Dx2"   : { "bits": 4, "read": true , "write": true  },
     90   "Dn"    : { "bits": 4, "read": true , "write": false },
     91   "Dn2"   : { "bits": 4, "read": true , "write": false },
     92   "Dn3"   : { "bits": 4, "read": true , "write": false },
     93   "Dn4"   : { "bits": 4, "read": true , "write": false },
     94   "Dm"    : { "bits": 4, "read": true , "write": false },
     95   "Ds"    : { "bits": 4, "read": true , "write": false },
     96   "Ds2"   : { "bits": 4, "read": true , "write": false },
     97   "Ds3"   : { "bits": 4, "read": true , "write": false },
     98   "Ds4"   : { "bits": 4, "read": true , "write": false },
     99   "DsList": { "bits": 4, "read": true , "write": false , "list": true },
    100 
    101   "Vd"    : { "bits": 4, "read": false, "write": true  },
    102   "Vd2"   : { "bits": 4, "read": false, "write": true  },
    103   "Vd3"   : { "bits": 4, "read": false, "write": true  },
    104   "Vd4"   : { "bits": 4, "read": false, "write": true  },
    105   "Vx"    : { "bits": 4, "read": true , "write": true  },
    106   "Vx2"   : { "bits": 4, "read": true , "write": true  },
    107   "Vn"    : { "bits": 4, "read": true , "write": false },
    108   "Vm"    : { "bits": 4, "read": true , "write": false },
    109   "Vs"    : { "bits": 4, "read": true , "write": false },
    110   "Vs2"   : { "bits": 4, "read": true , "write": false },
    111 };
    112 
    113 arm.FieldInfo = FieldInfo;
    114 
    115 // ARM utilities.
    116 class Utils {
    117   static splitInstructionSignature(s) {
    118     const names = s.match(/^[\w\|]+/)[0];
    119     s = s.substring(names.length);
    120 
    121     const opOffset = s.indexOf(" ")
    122     const suffix = s.substring(0, opOffset).trim();
    123     const operands = opOffset === -1 ? "" : s.substring(opOffset + 1).trim();
    124 
    125     return {
    126       names: names.split("|").map((base)=>{ return base + suffix}),
    127       operands: operands
    128     }
    129   }
    130 
    131   static parseShiftOp(s) {
    132     const m = s.match(/^(sop|lsl_or_asr|lsl|lsr|asr|ror|rrx) /);
    133     return m ? m[1] : "";
    134   }
    135 
    136   static parseDtArray(s) {
    137     const out = [];
    138     if (!s) return out;
    139 
    140     const arr = s.split("|");
    141     let i;
    142 
    143     // First expand anything between X-Y, for example s8-32 would be expanded to [s8, s16, s32].
    144     for (i = 0; i < arr.length; i++) {
    145       const v = arr[i];
    146 
    147       if (v.indexOf("-") !== -1) {
    148         const m = /^([A-Za-z]+)?(\d+)-(\d+)$/.exec(v);
    149         if (!m)
    150           FAIL(`Couldn't parse '${s}' data-type`);
    151 
    152         let type = m[1] || "";
    153         let size = parseInt(m[2], 10);
    154         let last = parseInt(m[3], 10);
    155 
    156         if (!Utils.checkDtSize(size) || !Utils.checkDtSize(last))
    157           FAIL(`Invalid dt width in '${s}'`);
    158 
    159         do {
    160           out.push(type + String(size));
    161           size <<= 1;
    162         } while (size <= last);
    163       }
    164       else {
    165         out.push(v);
    166       }
    167     }
    168 
    169     // Now expand 'x' to 's' and 'u'.
    170     i = 0;
    171     while (i < out.length) {
    172       const v = out[i];
    173       if (v.startsWith("x")) {
    174         out.splice(i, 1, "s" + v.substr(1), "u" + v.substr(1));
    175         i += 2;
    176       }
    177       else {
    178         i++;
    179       }
    180     }
    181 
    182     return out;
    183   }
    184 
    185   static checkDtSize(x) {
    186     return x === 8 || x === 16 || x === 32 || x === 64;
    187   }
    188 }
    189 arm.Utils = Utils;
    190 
    191 function normalizeNumber(n) {
    192   return n < 0 ? 0x100000000 + n : n;
    193 }
    194 
    195 function decomposeOperand(s) {
    196   const elementSuffix = "[#i]";
    197   let element = null;
    198   let consecutive = 0;
    199   let userRegList = false;
    200 
    201   if (s.endsWith("^")) {
    202     userRegList = true;
    203     s = s.substring(0, s.length - 1);
    204   }
    205 
    206   if (s.endsWith(elementSuffix)) {
    207     element = "#i";
    208     s = s.substring(0, s.length - elementSuffix.length);
    209   }
    210 
    211   if (s.endsWith("++")) {
    212     consecutive = 2;
    213     s = s.substr(0, s.length - 2);
    214   }
    215   else if (s.endsWith("+")) {
    216     consecutive = 1;
    217     s = s.substr(0, s.length - 1);
    218   }
    219 
    220   let m = s.match(/==|\!=|>=|<=|\*/);
    221   let restrict = false;
    222 
    223   if (m) {
    224     restrict = s.substr(m.index);
    225     s = s.substr(0, m.index);
    226   }
    227 
    228   return {
    229     data    : s,
    230     element : element,
    231     restrict: restrict,
    232     consecutive: consecutive,
    233     userRegList: true
    234   };
    235 }
    236 
    237 function splitOpcodeFields(s) {
    238   const arr = s.split("|");
    239   const out = [];
    240 
    241   for (let i = 0; i < arr.length; i++) {
    242     const val = arr[i];
    243     if (/^[0-1A-Z]{2,}$/.test(val))
    244       out.push.apply(out, val.match(/([0-1]+)|[A-Z]/g));
    245     else
    246       out.push(val);
    247   }
    248 
    249   return out.map((field) => { return field.trim(); });
    250 }
    251 
    252 // asmdb.aarch32.Operand
    253 // =====================
    254 
    255 // ARM operand.
    256 class Operand extends base.Operand {
    257   constructor(def) {
    258     super();
    259     this.data = def;
    260   }
    261 
    262   hasMemModes() {
    263     return Object.keys(this.memModes).length !== 0;
    264   }
    265 
    266   get name() {
    267     switch (this.type) {
    268       case "reg": return this.reg;
    269       case "mem": return this.mem;
    270       case "imm": return this.imm;
    271       case "rel": return this.rel;
    272       default   : return "";
    273     }
    274   }
    275 
    276   get scale() {
    277     if (this.restrict && this.restrict.startsWith("*"))
    278       return parseInt(this.restrict.substr(1), 10);
    279     else
    280       return 0;
    281   }
    282 
    283   isRelative() {
    284     if (this.type === "imm")
    285       return this.name === "relA" || this.name === "relS" || this.name === "relZ";
    286     else
    287       return false;
    288   }
    289 }
    290 arm.Operand = Operand;
    291 
    292 // asmdb.aarch32.Instruction
    293 // =========================
    294 
    295 function patternFromOperand(key) {
    296   return key;
    297   // return key.replace(/\b(?:[RVDS](?:d|s|n|m|x|x2))\b/, "R");
    298 }
    299 
    300 // Rewrite a memory operand expression (either base or index) to a simplified one, which is okay
    301 // to be generated as C++ expression. In general, we want to simplify != to a more favorable code.
    302 function simplifyMemoryExpression(e) {
    303   if (e.type === "binary" && e.op === "!=" && e.right.type === "var") {
    304     // Rewrite A != PC to A < PC
    305     if (e.right.name === "PC") { e.op = "<"; }
    306 
    307     // Rewrite A != HI to A < 8
    308     if (e.right.name === "HI") { e.op = "<"; e.right = exp.Imm(8); }
    309 
    310     // Rewrite A != XX to A < SP || A == LR
    311     if (e.right.name === "XX") {
    312       return exp.Or(exp.Lt(e.left, exp.Var("SP")),
    313                     exp.Eq(e.left.clone(), exp.Var("LR")));
    314     }
    315   }
    316 
    317   return e;
    318 }
    319 
    320 // ARM instruction.
    321 class Instruction extends base.Instruction {
    322   constructor(db, data) {
    323     super(db, data);
    324     // name, operands, encoding, opcode, metadata
    325 
    326     const encoding = Object.hasOwn(data, "a32") ? "a32" :
    327                      Object.hasOwn(data, "t32") ? "t32" :
    328                      Object.hasOwn(data, "t16") ? "t16" : "";
    329 
    330     this.name = data.name;
    331     this.it = dict();                // THUMB's 'it' flags.
    332     this.apsr = dict();
    333     this.fpcsr = dict();
    334     this.calc = dict();              // Calculations required to generate opcode.
    335     this.immCond = [];               // Immediate value conditions (array of conditions).
    336 
    337     this.s = null;                   // Instruction S flag (null, true, or false).
    338     this.dt = [];                    // Instruction <dt> field (first data-type).
    339     this.dt2 = [];                   // Instruction <dt2> field (second data-type).
    340 
    341     this.availableFrom  = "";        // Instruction supported by from  ARMv???.
    342     this.availableUntil = "";        // Instruction supported by until ARMv???.
    343 
    344     this._assignOperands(data.operands);
    345     this._assignEncoding(encoding.toUpperCase());
    346     this._assignOpcode(data[encoding]);
    347 
    348     for (let k in data) {
    349       if (k === "name" || k == encoding || k === "operands")
    350         continue;
    351       this._assignAttribute(k, data[k]);
    352     }
    353 
    354     this._updateOperandsInfo();
    355     this._postProcess();
    356   }
    357 
    358   _assignAttribute(key, value) {
    359     switch (key) {
    360       case "it":
    361         for (let it of value.split(" "))
    362           this.it[it.trim()] = true;
    363         break;
    364 
    365       case "apsr":
    366       case "fpcsr":
    367         this._assignAttributeKeyValue(key, value);
    368         break;
    369 
    370       case "imm":
    371         this.imm = exp.parse(value);
    372         break;
    373 
    374       case "calc":
    375         for (let calcKey in value)
    376           this.calc[calcKey] = exp.parse(value[calcKey]);
    377         break;
    378 
    379       default:
    380         super._assignAttribute(key, value);
    381     }
    382   }
    383 
    384   _assignAttributeKeyValue(name, content) {
    385     const attributes = content.trim().split(/[ ]+/);
    386 
    387     for (let i = 0; i < attributes.length; i++) {
    388       const attr = attributes[i].trim();
    389       if (!attr)
    390         continue;
    391 
    392       const eq = attr.indexOf("=");
    393       let key = eq === -1 ? attr : attr.substr(0, eq);
    394       let val = eq === -1 ? true : attr.substr(eq + 1);
    395 
    396       // If the key contains "|" it's a definition of multiple attributes.
    397       if (key.indexOf("|") !== -1) {
    398         const dot = key.indexOf(".");
    399 
    400         const base = dot === -1 ? "" : key.substr(0, dot + 1);
    401         const keys = (dot === -1 ? key : key.substr(dot + 1)).split("|");
    402 
    403         for (let j = 0; j < keys.length; j++)
    404           this[name][base + keys[j]] = val;
    405       }
    406       else {
    407         this[name][key] = val;
    408       }
    409     }
    410   }
    411 
    412   _assignEncoding(s) {
    413     this.arch = s === "T16" || s === "T32" ? "THUMB" : "ARM";
    414     this.encoding = s;
    415   }
    416 
    417   _assignOperands(s) {
    418     if (!s) return;
    419 
    420     // Split into individual operands and push them to `operands`.
    421     const arr = base.Parsing.splitOperands(s);
    422     for (let i = 0; i < arr.length; i++) {
    423       let def = arr[i];
    424       const op = new Operand(def);
    425 
    426       const consecutive = def.match(/(\d+)x\{(.*)\}([+][+]?)/);
    427       if (consecutive)
    428         def = consecutive[2];
    429 
    430       op.sign = false;
    431       op.element = null;
    432       op.shiftOp = "";
    433       op.shiftImm = null;
    434 
    435       // Handle {optional} attribute.
    436       if (Parsing.isOptional(def)) {
    437         op.optional = true;
    438         def = Parsing.clearOptional(def);
    439       }
    440 
    441       // Handle commutativity <-> symbol.
    442       if (Parsing.isCommutative(def)) {
    443         op.commutative = true;
    444         def = Parsing.clearCommutative(def);
    445       }
    446 
    447       // Handle shift operation.
    448       let shiftOp = Utils.parseShiftOp(def);
    449       if (shiftOp) {
    450         op.shiftOp = shiftOp;
    451         def = def.substring(shiftOp.length + 1);
    452       }
    453 
    454       if (def.startsWith("[")) {
    455         op.type = "mem";
    456         op.memModes = dict();
    457 
    458         op.base = null;
    459         op.index = null;
    460         op.offset = null;
    461 
    462         let mem = def;
    463         let didHaveMemMode = false;
    464 
    465         for (;;) {
    466           if (mem.endsWith("!")) {
    467             op.memModes.preIndex = true;
    468             mem = mem.substring(0, mem.length - 1);
    469 
    470             didHaveMemMode = true;
    471             break;
    472           }
    473 
    474           if (mem.endsWith("@")) {
    475             op.memModes.postIndex = true;
    476             mem = mem.substring(0, mem.length - 1);
    477 
    478             didHaveMemMode = true;
    479             break;
    480           }
    481 
    482           if (mem.endsWith("{!}")) {
    483             op.memModes.offset = true;
    484             op.memModes.preIndex = true;
    485             mem = mem.substring(0, mem.length - 3);
    486 
    487             didHaveMemMode = true;
    488             continue;
    489           }
    490 
    491           if (mem.endsWith("{@}")) {
    492             op.memModes.offset = true;
    493             op.memModes.postIndex = true;
    494             mem = mem.substring(0, mem.length - 3);
    495 
    496             didHaveMemMode = true;
    497             continue;
    498           }
    499 
    500           break;
    501         }
    502 
    503         if (!mem.endsWith("]"))
    504           FAIL(`Unknown memory operand '${mem}' in '${def}'`);
    505 
    506         let parts = mem.substring(1, mem.length - 1).split(",").map(function(s) { return s.trim() });
    507         for (let i = 0; i < parts.length; i++) {
    508           const part = parts[i];
    509 
    510           const m = part.match(/^\{(lsl|sop)\s+#(\w+)\}$/);
    511           if (m) {
    512             op.shiftOp = m[1];
    513             op.shiftImm = m[2];
    514             continue;
    515           }
    516 
    517           if (i === 0) {
    518             op.base = dict();
    519             op.base.field = part;
    520             op.base.exp = null;
    521 
    522             const m = part.match(/^([A-Za-z]\w*)/);
    523             if (m.length < part.length) {
    524               op.base.exp = simplifyMemoryExpression(exp.parse(part));
    525               op.base.field = m[1];
    526             }
    527           }
    528           else if (part.startsWith("#")) {
    529             let p = part.substring(1);
    530             let u = "1";
    531             let alwaysNegative = false;
    532 
    533             let offExp = null;
    534             let offMul = 1;
    535 
    536             if (p.startsWith("+/-")) {
    537               u = "U";
    538               p = p.substring(3);
    539             }
    540 
    541             if (p.startsWith("-")) {
    542               alwaysNegative = false;
    543               p = p.substring(1);
    544             }
    545 
    546             const expMatch = p.match(/^([A-Za-z]\w*)==/);
    547             if (expMatch) {
    548               offExp = exp.parse(p);
    549               p = p.substr(0, expMatch[1].length);
    550             }
    551 
    552             const mulMatch = p.match(/\s*\*\s*(\d+)$/);
    553             if (mulMatch) {
    554               offMul = parseInt(mulMatch[1]);
    555               p = p.substr(0, mulMatch.index);
    556             }
    557 
    558             op.offset = dict();
    559             op.offset.field = p;
    560             op.offset.u = u;
    561             op.offset.exp = offExp;
    562             op.offset.mul = offMul;
    563             op.offset.negative = alwaysNegative;
    564           }
    565           else {
    566             let p = part;
    567             let u = "1";
    568 
    569             if (p.startsWith("+/-")) {
    570               u = "U";
    571               p = p.substring(3);
    572             }
    573 
    574             op.index = dict();
    575             op.index.field = p;
    576             op.index.u = u;
    577 
    578             const m = p.match(/^([A-Za-z]\w*)/);
    579             if (m.length < p.length) {
    580               op.index.exp = simplifyMemoryExpression(exp.parse(p));
    581               op.index.field = m[1];
    582             }
    583           }
    584         }
    585 
    586         if (!op.hasMemModes() && (op.offset || op.index))
    587           op.memModes.offset = true;
    588 
    589         op.mem = mem;
    590       }
    591       else if (def.startsWith("#")) {
    592         const obj = decomposeOperand(def);
    593         const imm = obj.data;
    594 
    595         op.type = "imm";
    596         op.imm = imm.substring(1);        // Immediate operand name.
    597         op.immSize = 0;                   // Immediate size in bits.
    598         op.restrict = obj.restrict;       // Immediate condition.
    599       }
    600       else {
    601         const obj = decomposeOperand(def);
    602         const reg = obj.data;
    603 
    604         const type = reg.substr(0, 1).toLowerCase();
    605         const info = FieldInfo[reg];
    606 
    607         if (!info)
    608           FAIL(`Unknown register operand '${reg}' in '${def}'`);
    609 
    610         op.type     = info.list ? "reg-list" : "reg";
    611         op.reg      = reg;                // Register name (as specified in manual).
    612         op.regType  = type;               // Register type.
    613         op.regList  = !!info.list;        // Register list.
    614         op.read     = info.read;          // Register access (read).
    615         op.write    = info.write;         // Register access (write).
    616         op.element  = obj.element;        // Register element[] access.
    617         op.restrict = obj.restrict;       // Register condition.
    618         op.consecutive = obj.consecutive;
    619       }
    620 
    621       this.operands.push(op);
    622 
    623       if (consecutive) {
    624         const count = parseInt(consecutive[1]);
    625         for (let n = 2; n <= count; n++) {
    626           const def = consecutive[3].replace(op.reg, op.reg + n);
    627           const opN = new Operand(def);
    628           opN.type = "reg";
    629           opN.reg = op.reg + n;
    630           opN.regType = op.regType;
    631           opN.read = op.read;
    632           opN.write = op.write;
    633           opN.element = op.element;
    634           opN.consecutive = consecutive[3].length;
    635           this.operands.push(opN);
    636         }
    637       }
    638     }
    639   }
    640 
    641   _assignOpcode(s) {
    642     this.opcodeString = s;
    643 
    644     let opcodeIndex = 0;
    645     let opcodeValue = 0;
    646 
    647     let patternMap = {};
    648 
    649     // Split opcode into its fields.
    650     const arr = splitOpcodeFields(s);
    651     const dup = dict();
    652 
    653     const fields = this.fields;
    654     const pattern = [];
    655 
    656     const fieldMap = Object.create(null);
    657     for (let field of arr) {
    658       fieldMap[field] = true;
    659     }
    660 
    661     for (let i = arr.length - 1; i >= 0; i--) {
    662       let key = arr[i];
    663       let m;
    664 
    665       if (/^[0-1]+$/.test(key)) {
    666         // This part of the opcode is RAW bits, they contribute to the `opcodeValue`.
    667         opcodeValue |= parseInt(key, 2) << opcodeIndex;
    668         opcodeIndex += key.length;
    669         pattern.unshift("_".repeat(key.length));
    670       }
    671       else {
    672         pattern.unshift(patternFromOperand(key));
    673         patternMap[patternFromOperand(key)] = true;
    674 
    675         let size = 0;
    676         let mask = 0;
    677         let bits = 0;
    678         let from = -1;
    679 
    680         let lbit = key.startsWith("'");
    681         let hbit = key.endsWith("'");
    682 
    683         if ((m = key.match(/\[\s*(\d+)\s*\:\s*(\d+)\s*\]$/))) {
    684           const a = parseInt(m[1], 10);
    685           const b = parseInt(m[2], 10);
    686           if (a < b)
    687             FAIL(`Invalid bit range '${key}' in opcode '${s}'`);
    688           from = b;
    689           size = a - b + 1;
    690           mask = ((1 << size) - 1) << b;
    691           key = key.substr(0, m.index).trim();
    692         }
    693         else if ((m = key.match(/\[\s*(\d+)\s*\]$/))) {
    694           from = parseInt(m[1], 10);
    695           size = 1;
    696           mask = 1 << from;
    697           key = key.substr(0, m.index).trim();
    698         }
    699         else if ((m = key.match(/\:\s*(\d+)$/))) {
    700           size = parseInt(m[1], 10);
    701           bits = size;
    702           key = key.substr(0, m.index).trim();
    703         }
    704         else {
    705           const key_ = key;
    706 
    707           if (lbit || hbit) {
    708             from = 0;
    709 
    710             if (lbit && hbit)
    711               FAIL(`Couldn't recognize the format of '${key}' in opcode '${s}'`);
    712 
    713             if (lbit) {
    714               key = key.substring(1);
    715             }
    716 
    717             if (hbit) {
    718               key = key.substring(0, key.length - 1);
    719               from = 4;
    720             }
    721 
    722             size = 1;
    723           }
    724           else if (FieldInfo[key]) {
    725             // Sizes of some standard fields can be assigned automatically.
    726             size = FieldInfo[key].bits;
    727             bits = size;
    728 
    729             if (fieldMap["'" + key])
    730               from = 1;
    731           }
    732           else if (key.length === 1) {
    733             // Sizes of one-letter fields (like 'U', 'F', etc...) is 1 if not specified.
    734             size = 1;
    735             bits = 1;
    736           }
    737           else {
    738             FAIL(`Couldn't recognize the size of '${key}' in opcode '${s}'`);
    739           }
    740 
    741           if (dup[key_] === true) {
    742             bits = 0;
    743             lbit = 0;
    744             hbit = 0;
    745           }
    746           else {
    747             dup[key_] = true;
    748           }
    749         }
    750 
    751         let field = fields[key];
    752         if (!field) {
    753           field = {
    754             index: opcodeIndex,
    755             values: [],
    756             bits: 0,
    757             mask: 0,
    758             lbit: 0,
    759             hbit: 0 // Only 1 if a single quote (') was used.
    760           }
    761           fields[key] = field;
    762         }
    763 
    764         if (from === -1)
    765           from = field.bits;
    766 
    767         field.mask |= mask;
    768         field.bits += bits;
    769         field.lbit += lbit;
    770         field.hbit += hbit;
    771         field.values.push({
    772           index: opcodeIndex,
    773           from: from,
    774           size: size
    775         });
    776 
    777         opcodeIndex += size;
    778       }
    779     }
    780 
    781     for (let i = 0; i < pattern.length; i++)
    782       if (pattern[i] === 'U')
    783         pattern[i] = "_";
    784 
    785     // Normalize all fields.
    786     for (let key in fields) {
    787       const field = fields[key];
    788 
    789       // There should be either number of bits or mask, there shouldn't be both.
    790       if (!field.bits && !field.mask)
    791         FAIL(`Part '${key}' of opcode '${s}' contains neither size nor mask`);
    792 
    793       if (field.bits && field.mask)
    794         FAIL(`Part '${key}' of opcode '${s}' contains both size and mask`);
    795 
    796       if (field.bits)
    797         field.mask = ((1 << field.bits) - 1);
    798       else if (field.mask)
    799         field.bits = 32 - Math.clz32(field.mask);
    800 
    801       // Handle field that used single-quote.
    802       if (field.lbit) {
    803         field.mask = (field.mask << 1) | 0x1;
    804         field.bits++;
    805       }
    806 
    807       if (field.hbit) {
    808         field.mask |= 1 << field.bits;
    809         field.bits++;
    810       }
    811 
    812       const op = this.operandByName(key);
    813       if (op && op.isImm())
    814         op.immSize = field.bits;
    815     }
    816 
    817     // Check if the opcode value has the correct number of bits (either 16 or 32).
    818     if (opcodeIndex !== 16 && opcodeIndex !== 32)
    819       FAIL(`The number of bits '${opcodeIndex}' used by the opcode '${s}' doesn't match 16 or 32`);
    820     this.opcodeValue = normalizeNumber(opcodeValue);
    821   }
    822 
    823   _assignSpecificAttribute(key, value) {
    824     // Support ARMv?+ and ARMv?- attributes.
    825     if (/^ARM\w+[+-]$/.test(key)) {
    826       const armv = key.substr(0, key.length - 1);
    827       const sign = key.substr(key.length - 1);
    828 
    829       if (sign === "+")
    830         this.availableFrom = armv;
    831       else
    832         this.availableUntil = armv;
    833       return true;
    834     }
    835 
    836     switch (key) {
    837       case "it": {
    838         const values = String(value).split("|");
    839         for (let i = 0; i < values.length; i++) {
    840           const value = values[i];
    841           switch (value) {
    842             case "in"  : this.it.IN   = true; break;
    843             case "out" : this.it.OUT  = true; break;
    844             case "any" : this.it.IN   = true;
    845                          this.it.OUT  = true; break;
    846             case "last": this.it.LAST = true; break;
    847             case "def" : this.it.DEF  = true; break;
    848             default:
    849               this.report(`${this.name}: Unhandled IT value '${value}'`);
    850           }
    851         }
    852         return true;
    853       }
    854     }
    855 
    856     return false;
    857   }
    858 
    859   // ARM instruction name could consist of name and optional type information
    860   // specified as <dt> and <dt2> in ARM manuals. We parse this information and
    861   // store it to `dt` and `dt2` fields. In addition, we also recognize the `S`
    862   // suffix (uppercase) of the instruction and mark it as `S` instruction. After
    863   // that the name is normalized to be lowercased.
    864   //
    865   // This functionality requires all the instruction data to be already set-up.
    866   _postProcess() {
    867     let s = this.name;
    868 
    869     // Parse <dt> and <dt2> fields.
    870     if (s.indexOf(".") !== -1) {
    871       const parts = s.split(".");
    872       this.name = parts[0];
    873 
    874       if (parts.length > 3)
    875         FAIL(`Couldn't recognize name attributes of '${s}'`);
    876 
    877       for (let i = 1; i < parts.length; i++) {
    878         const dt = Utils.parseDtArray(parts[i]);
    879         if (i === 1)
    880           this.dt = dt;
    881         else
    882           this.dt2 = dt;
    883       }
    884     }
    885 
    886     // Recognize "S" suffix.
    887     if (this.name.endsWith("S")) {
    888       this.name = this.name.substr(0, this.name.length - 1) + "s";
    889       this.s = true;
    890     }
    891 
    892     this.dt.sort();
    893   }
    894 
    895   operandByName(name) {
    896     const operands = this.operands;
    897     for (let i = 0; i < operands.length; i++) {
    898       const op = operands[i];
    899       if (op.name === name)
    900         return op;
    901     }
    902     return null;
    903   }
    904 }
    905 arm.Instruction = Instruction;
    906 
    907 // asmdb.aarch32.ISA
    908 // =================
    909 
    910 function mergeGroupData(data, group) {
    911   for (let k in group) {
    912     switch (k) {
    913       case "group":
    914       case "data":
    915         break;
    916 
    917       case "ext":
    918         data[k] = (data[k] ? data[k] + " " : "") + group[k];
    919         break;
    920 
    921       default:
    922         if (data[k] === undefined)
    923           data[k] = group[k]
    924         break;
    925     }
    926   }
    927 }
    928 
    929 class ISA extends base.ISA {
    930   constructor(data) {
    931     super(data);
    932     this.addData(data || NONE);
    933   }
    934 
    935   _addInstructions(groups) {
    936     for (let group of groups) {
    937       for (let inst of group.data) {
    938         const sgn = Utils.splitInstructionSignature(inst.inst);
    939         const data = MapUtils.cloneExcept(inst, { "inst": true });
    940 
    941         mergeGroupData(data, group)
    942 
    943         for (let j = 0; j < sgn.names.length; j++) {
    944           data.name = sgn.names[j];
    945           data.operands = sgn.operands;
    946           if (j > 0)
    947             data.aliasOf = sgn.names[0];
    948           this._addInstruction(new Instruction(this, data));
    949         }
    950       }
    951     }
    952 
    953     return this;
    954   }
    955 /*
    956   _addInstructions(instructions) {
    957     for (let i = 0; i < instructions.length; i++) {
    958       const obj = instructions[i];
    959       const sgn = obj.inst;
    960       const sep = sgn.indexOf(" ");
    961 
    962       const names = (sep !== -1 ? sgn.substring(0, sep) : sgn).trim().split("/");
    963       const operands = sep !== -1 ? sgn.substring(sep + 1) : "";
    964 
    965       const encoding = Object.hasOwn(obj, "a32") ? "a32" :
    966                        Object.hasOwn(obj, "t32") ? "t32" :
    967                        Object.hasOwn(obj, "t16") ? "t16" : "";
    968 
    969       if (!encoding)
    970         FAIL(`Instruction ${names.join("/")} doesn't encoding, it must provide either a32, t32, or t16 field`);
    971 
    972       for (let j = 0; j < names.length; j++) {
    973         const inst = new Instruction(this, names[j], operands, encoding.toUpperCase(), obj[encoding], obj);
    974         if (j > 0)
    975           inst.aliasOf = names[0];
    976         this._addInstruction(inst);
    977       }
    978     }
    979 
    980     return this;
    981   }
    982 */
    983 }
    984 arm.ISA = ISA;
    985 
    986 }).apply(this, typeof module === "object" && module && module.exports
    987   ? [module, "exports"] : [this.asmdb || (this.asmdb = {}), "aarch32"]);