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"]);