diff --git a/compiler/src/.dscanner.ini b/compiler/src/.dscanner.ini index 7d9344b6c1e4..4016276f4b65 100644 --- a/compiler/src/.dscanner.ini +++ b/compiler/src/.dscanner.ini @@ -127,6 +127,9 @@ unused_variable_check="-dmd.backend.aarray,\ -dmd.backend.cgelem,\ -dmd.backend.cgobj,\ -dmd.backend.cgsched,\ +-dmd.backend.arm.cod2,\ +-dmd.backend.arm.cod3,\ +-dmd.backend.arm.disasmarm,\ -dmd.backend.x86.cgxmm,\ -dmd.backend.x86.cod1,\ -dmd.backend.x86.cod2,\ diff --git a/compiler/src/build.d b/compiler/src/build.d index 035af27b1a94..71748eddef09 100755 --- a/compiler/src/build.d +++ b/compiler/src/build.d @@ -1623,6 +1623,7 @@ auto sourceFiles() x86/cod3.d cv8.d dcgcv.d pdata.d util2.d var.d backconfig.d drtlsym.d dwarfeh.d ptrntab.d dvarstats.d dwarfdbginf.d cgen.d goh.d barray.d cgcse.d elpicpie.d machobj.d elfobj.d mscoffobj.d filespec.d cgobj.d aarray.d disasm86.d + arm/cod2.d arm/cod3.d arm/disasmarm.d " ), }; diff --git a/compiler/src/dmd/backend/arm/cod2.d b/compiler/src/dmd/backend/arm/cod2.d new file mode 100644 index 000000000000..a090f01f3967 --- /dev/null +++ b/compiler/src/dmd/backend/arm/cod2.d @@ -0,0 +1,160 @@ +/** + * Code generation 2 + * + * Includes: + * - math operators (+ - * / %) and functions (abs, cos, sqrt) + * - 'string' functions (strlen, memcpy, memset) + * - pointers (address of / dereference) + * - struct assign, constructor, destructor + * + * Compiler implementation of the + * $(LINK2 https://www.dlang.org, D programming language). + * + * Copyright: Copyright (C) 1984-1998 by Symantec + * Copyright (C) 2000-2024 by The D Language Foundation, All Rights Reserved + * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) + * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/backend/arm/cod2.d, backend/cod2.d) + * Documentation: https://dlang.org/phobos/dmd_backend_arm_cod2.html + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/backend/arm/cod2.d + */ + +module dmd.backend.arm.cod2; + +import core.stdc.stdio; +import core.stdc.stdlib; +import core.stdc.string; + +import dmd.backend.cc; +import dmd.backend.cdef; +import dmd.backend.code; +import dmd.backend.x86.code_x86; +import dmd.backend.codebuilder; +import dmd.backend.mem; +import dmd.backend.el; +import dmd.backend.global; +import dmd.backend.oper; +import dmd.backend.ty; +import dmd.backend.type; +import dmd.backend.x86.xmm; + + +nothrow: +@safe: + +import dmd.backend.cg : segfl, stackfl; + +__gshared int cdcmp_flag; + +import dmd.backend.divcoeff : choose_multiplier, udiv_coefficients; + +/***************************** + * Handle operators which are more or less orthogonal + * OPadd, OPmin, OPand, OPor, OPxor + */ + +@trusted +void cdorth(ref CGstate cg, ref CodeBuilder cdb,elem *e,regm_t *pretregs) +{ + //printf("cdorth(e = %p, *pretregs = %s)\n",e,regm_str(*pretregs)); + + elem* e1 = e.E1; + elem* e2 = e.E2; + if (*pretregs == 0) // if don't want result + { + codelem(cg,cdb,e1,pretregs,false); // eval left leaf + *pretregs = 0; // in case they got set + codelem(cg,cdb,e2,pretregs,false); + return; + } + + const ty = tybasic(e.Ety); + const ty1 = tybasic(e1.Ety); + const ty2 = tybasic(e2.Ety); + const sz = _tysize[ty]; + + if (tyfloating(ty1)) + { + assert(0); + } + + regm_t posregs = cg.allregs; + + regm_t retregs1 = posregs; + + codelem(cg, cdb, e1, &retregs1, false); + regm_t retregs2 = cg.allregs & ~retregs1; + scodelem(cg, cdb, e2, &retregs2, retregs1, false); + + regm_t retregs = *pretregs & cg.allregs; + if (retregs == 0) /* if no return regs speced */ + /* (like if wanted flags only) */ + retregs = ALLREGS & posregs; // give us some + reg_t Rd = allocreg(cdb, retregs, ty); + + reg_t Rn = findreg(retregs1); + reg_t Rm = findreg(retregs2); + + uint sf = sz == 8; + uint op = 0; + uint S = (*pretregs & mPSW) != 0; + uint opcode = 0xB; + uint opt = 0; + uint option = tyToExtend(ty); + uint imm3 = 0; + switch (e.Eoper) + { + case OPadd: op = 0; break; + case OPmin: op = 1; break; + default: + assert(0); + } + uint ins = (sf << 31) | + (op << 30) | + (0xB << 24) | + (opt << 22) | + (1 << 21) | + (Rm << 16) | + (option << 13) | + (imm3 << 10) | + (Rn << 5) | + Rd; + cdb.gen1(ins); + + fixresult(cdb,e,mask(Rd),*pretregs); +} + +/************************************************* + * Convert from ty to type according to table: + + 0 UXTB + 1 UXTH + 2 UXTW + 3 LSL|UXTX + 4 SXTB + 5 SXTH + 6 SXTW + 7 SXTX + * Params: + * ty = basic ty + * Output: + * + */ +uint tyToExtend(tym_t ty) +{ + assert(tyintegral(ty)); + uint extend; + uint sz = tysize(ty); + switch (sz) + { + case 1: extend = 0; break; + case 2: extend = 1; break; + case 4: extend = 2; break; + case 8: extend = 3; break; + default: + assert(0); + } + if (!tyuns(ty)) + extend |= 4; + return extend; +} diff --git a/compiler/src/dmd/backend/arm/cod3.d b/compiler/src/dmd/backend/arm/cod3.d new file mode 100644 index 000000000000..4c9be123695b --- /dev/null +++ b/compiler/src/dmd/backend/arm/cod3.d @@ -0,0 +1,446 @@ +/** + * Code generation 3 + * + * Includes: + * - generating a function prolog (pushing return address, loading paramters) + * - generating a function epilog (restoring registers, returning) + * - generation / peephole optimizations of jump / branch instructions + * + * Compiler implementation of the + * $(LINK2 https://www.dlang.org, D programming language). + * + * Copyright: Copyright (C) 1994-1998 by Symantec + * Copyright (C) 2000-2024 by The D Language Foundation, All Rights Reserved + * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) + * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/backend/arm/cod3.d, backend/cod3.d) + * Documentation: https://dlang.org/phobos/dmd_backend_arm_cod3.html + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/backend/arm/cod3.d + */ + +module dmd.backend.arm.cod3; + +import core.bitop; +import core.stdc.stdio; +import core.stdc.stdlib; +import core.stdc.string; + +import dmd.backend.barray; +import dmd.backend.cc; +import dmd.backend.cdef; +import dmd.backend.cgcse; +import dmd.backend.code; +import dmd.backend.x86.code_x86; +import dmd.backend.x86.cod3; +import dmd.backend.codebuilder; +import dmd.backend.dlist; +import dmd.backend.dvec; +import dmd.backend.melf; +import dmd.backend.mem; +import dmd.backend.el; +import dmd.backend.global; +import dmd.backend.obj; +import dmd.backend.oper; +import dmd.backend.rtlsym; +import dmd.backend.symtab; +import dmd.backend.ty; +import dmd.backend.type; +import dmd.backend.x86.xmm; + + +nothrow: +@safe: + +enum INSTR : uint +{ + ret = 0xd65f03c0, +} + +enum MARS = true; + +/******************************* + * Generate and return function epilog. + * Params: + * b = block that returns + * Output: + * cgstate.retsize Size of function epilog + */ + +@trusted +void epilog(block *b) +{ + code *cpopds; + reg_t reg; + reg_t regx; // register that's not a return reg + regm_t topop,regm; + targ_size_t xlocalsize = localsize; + + CodeBuilder cdbx; cdbx.ctor(); + tym_t tyf = funcsym_p.ty(); + tym_t tym = tybasic(tyf); + bool farfunc = tyfarfunc(tym) != 0; + if (!(b.Bflags & BFL.epilog)) // if no epilog code + goto Lret; // just generate RET + regx = (b.BC == BCret) ? AX : CX; + + cgstate.retsize = 0; + + if (tyf & mTYnaked) // if no prolog/epilog + return; + + if (config.flags & CFGtrace && + (!(config.flags4 & CFG4allcomdat) || + funcsym_p.Sclass == SC.comdat || + funcsym_p.Sclass == SC.global || + (config.flags2 & CFG2comdat && SymInline(funcsym_p)) + ) + ) + { + Symbol *s = getRtlsym(farfunc ? RTLSYM.TRACE_EPI_F : RTLSYM.TRACE_EPI_N); + makeitextern(s); + cdbx.gencs(I16 ? 0x9A : CALL,0,FLfunc,s); // CALLF _trace + code_orflag(cdbx.last(),CFoff | CFselfrel); + useregs((ALLREGS | mBP | mES) & ~s.Sregsaved); + } + + if (cgstate.usednteh & (NTEH_try | NTEH_except | NTEHcpp | EHcleanup | EHtry | NTEHpassthru) && (config.exe == EX_WIN32 || MARS)) + { + nteh_epilog(cdbx); + } + + cpopds = null; + + /* Pop all the general purpose registers saved on the stack + * by the prolog code. Remember to do them in the reverse + * order they were pushed. + */ + topop = fregsaved & ~cgstate.mfuncreg; +// epilog_restoreregs(cdbx, topop); // implement + + if (cgstate.usednteh & NTEHjmonitor) + { + regm_t retregs = 0; + if (b.BC == BCretexp) + retregs = regmask(b.Belem.Ety, tym); + nteh_monitor_epilog(cdbx,retregs); + xlocalsize += 8; + } + + if (cgstate.needframe || (xlocalsize && cgstate.hasframe)) + { + assert(cgstate.hasframe); + if (xlocalsize || cgstate.enforcealign) + { + if (config.flags2 & CFG2stomp) + { /* MOV ECX,0xBEAF + * L1: + * MOV [ESP],ECX + * ADD ESP,4 + * CMP EBP,ESP + * JNE L1 + * POP EBP + */ + /* Value should be: + * 1. != 0 (code checks for null pointers) + * 2. be odd (to mess up alignment) + * 3. fall in first 64K (likely marked as inaccessible) + * 4. be a value that stands out in the debugger + */ + assert(I32 || I64); + targ_size_t value = 0x0000BEAF; + reg_t regcx = CX; + cgstate.mfuncreg &= ~mask(regcx); + uint grex = I64 ? REX_W << 16 : 0; + cdbx.genc2(0xC7,grex | modregrmx(3,0,regcx),value); // MOV regcx,value + cdbx.gen2sib(0x89,grex | modregrm(0,regcx,4),modregrm(0,4,SP)); // MOV [ESP],regcx + code *c1 = cdbx.last(); + cdbx.genc2(0x81,grex | modregrm(3,0,SP),REGSIZE); // ADD ESP,REGSIZE + genregs(cdbx,0x39,SP,BP); // CMP EBP,ESP + if (I64) + code_orrex(cdbx.last(),REX_W); + genjmp(cdbx,JNE,FLcode,cast(block *)c1); // JNE L1 + // explicitly mark as short jump, needed for correct retsize calculation (Bugzilla 15779) + cdbx.last().Iflags &= ~CFjmp16; + cdbx.gen1(0x58 + BP); // POP BP + } + else if (config.exe == EX_WIN64) + { // See https://msdn.microsoft.com/en-us/library/tawsa7cb%28v=vs.100%29.aspx + // LEA RSP,0[RBP] + cdbx.genc1(LEA,(REX_W<<16)|modregrm(2,SP,BPRM),FLconst,0); + cdbx.gen1(0x58 + BP); // POP RBP + } + else + { + genregs(cdbx,0x8B,SP,BP); // MOV SP,BP + if (I64) + code_orrex(cdbx.last(), REX_W); // MOV RSP,RBP + cdbx.gen1(0x58 + BP); // POP BP + } + } + else + cdbx.gen1(0x58 + BP); // POP BP + } + else if (xlocalsize == REGSIZE) + { + cgstate.mfuncreg &= ~mask(regx); + cdbx.gen1(0x58 + regx); // POP regx + } + else if (xlocalsize) + cod3_stackadj(cdbx, cast(int)-xlocalsize); + + if (b.BC == BCret || b.BC == BCretexp) + { +Lret: + opcode_t op = INSTR.ret; + if (!typfunc(tym) || // if caller cleans the stack + config.exe == EX_WIN64 || + cgstate.Para.offset == 0) // or nothing pushed on the stack anyway + { + cdbx.gen1(INSTR.ret); // RET + } + else + { // Stack is always aligned on register size boundary + cgstate.Para.offset = (cgstate.Para.offset + (REGSIZE - 1)) & ~(REGSIZE - 1); + if (cgstate.Para.offset >= 0x10000) + { + /* + POP REG + ADD ESP, Para.offset + JMP REG + */ + cdbx.gen1(0x58+regx); + cdbx.genc2(0x81, modregrm(3,0,SP), cgstate.Para.offset); + if (I64) + code_orrex(cdbx.last(), REX_W); + cdbx.genc2(0xFF, modregrm(3,4,regx), 0); + if (I64) + code_orrex(cdbx.last(), REX_W); + } + else + cdbx.genc2(op,0,cgstate.Para.offset); // RET Para.offset + } + } + + // If last instruction in ce is ADD SP,imm, and first instruction + // in c sets SP, we can dump the ADD. + CodeBuilder cdb; cdb.ctor(); + cdb.append(b.Bcode); + code *cr = cdb.last(); + code *c = cdbx.peek(); + + //pinholeopt(c, null); + cgstate.retsize += calcblksize(c); // compute size of function epilog + cdb.append(cdbx); + b.Bcode = cdb.finish(); +} + +/******************************* + * Calculate bl.Bsize. + */ + +uint calcblksize(code *c) +{ + uint size = 0; + for (; c; c = code_next(c)) + size += 4; + //printf("calcblksize(c = x%x) = %d\n", c, size); + return size; +} + +/***************************** + * Calculate and return code size of a code. + * Note that NOPs are sometimes used as markers, but are + * never output. LINNUMs are never output. + * Note: This routine must be fast. Profiling shows it is significant. + */ + +@trusted +uint calccodsize(code *c) +{ + return 4; +} + + +/************************** + * Convert instructions to object code and write them to objmod. + * Params: + * seg = code segment to write to, code starts at Offset(seg) + * c = list of instructions to write + * disasmBuf = if not null, then also write object code here + * Returns: + * offset of end of code emitted + */ + +@trusted +uint codout(int seg, code *c, Barray!ubyte* disasmBuf) +{ + code *cn; + uint flags; + Symbol *s; + + debug + if (debugc) printf("codout(%p), Coffset = x%llx\n",c,cast(ulong)Offset(seg)); + + MiniCodeBuf ggen = void; + ggen.index = 0; + ggen.offset = cast(uint)Offset(seg); + ggen.seg = seg; + ggen.disasmBuf = disasmBuf; + + for (; c; c = code_next(c)) + { + debug + { + if (debugc) { printf("off=%02x, sz=%d, ",cast(int)ggen.getOffset(),cast(int)calccodsize(c)); code_print(c); } + uint startOffset = ggen.getOffset(); + } + + opcode_t op = c.Iop; + switch (op & 0xFF) + { + case PSOP.root: + /* Check for SSE4 opcode v/pmaxuw xmm1,xmm2/m128 */ + if(op == 0x660F383E || c.Iflags & CFvex) break; + + switch (op) + { case PSOP.linnum: + /* put out line number stuff */ + objmod.linnum(c.IEV1.Vsrcpos,seg,ggen.getOffset()); + break; + case PSOP.adjesp: + //printf("adjust ESP %ld\n", cast(long)c.IEV1.Vint); + break; + + default: + break; + } + + debug + assert(calccodsize(c) == 0); + + continue; + + case NOP: /* don't send them out */ + if (op != NOP) + break; + debug + assert(calccodsize(c) == 0); + + continue; + + case ASM: + if (op != ASM) + break; + ggen.flush(); + if (c.Iflags == CFaddrsize) // kludge for DA inline asm + { + //do32bit(ggen, FLblockoff,c.IEV1,0,0); + assert(0); + } + else + { + ggen.offset += objmod.bytes(seg,ggen.offset,cast(uint)c.IEV1.len,c.IEV1.bytes); + } + debug + assert(calccodsize(c) == c.IEV1.len); + + continue; + + default: + break; + } + flags = c.Iflags; + + // See if we need to flush (don't have room for largest code sequence) + if (ggen.available() < 4) + ggen.flush(); + + ggen.gen32(op); + } + ggen.flush(); + Offset(seg) = ggen.offset; + //printf("-codout(), Coffset = x%x\n", Offset(seg)); + return cast(uint)ggen.offset; /* ending address */ +} + + +/*************************** + * Debug code to dump code structure. + */ + +void WRcodlst(code *c) +{ + for (; c; c = code_next(c)) + code_print(c); +} + +@trusted +void code_print(scope code* c) +{ + if (c == null) + { + printf("code 0\n"); + return; + } + + printf("code %p: nxt=%p op=%08x",c,code_next(c),c.Iop); + + if ((c.Iop & PSOP.mask) == PSOP.root) + { + if (c.Iop == PSOP.linnum) + { + printf(" linnum = %d\n",c.IEV1.Vsrcpos.Slinnum); + return; + } + printf(" PSOP"); + } + + if (c.Iflags) + printf(" flg=%x",c.Iflags); + if (0) + { + switch (c.IFL1) + { + case FLconst: + case FLoffset: + printf(" int = %4d",c.IEV1.Vuns); + break; + + case FLblock: + printf(" block = %p",c.IEV1.Vblock); + break; + + case FLswitch: + case FLblockoff: + case FLlocalsize: + case FLframehandler: + case 0: + break; + + case FLdatseg: + printf(" FLdatseg %d.%llx",c.IEV1.Vseg,cast(ulong)c.IEV1.Vpointer); + break; + + case FLauto: + case FLfast: + case FLreg: + case FLdata: + case FLudata: + case FLpara: + case FLbprel: + case FLtlsdata: + case FLextern: + printf(" "); + WRFL(c.IFL1); + printf(" sym='%s'",c.IEV1.Vsym.Sident.ptr); + if (c.IEV1.Voffset) + printf(".%d", cast(int)c.IEV1.Voffset); + break; + + default: + WRFL(c.IFL1); + break; + } + } + printf("\n"); +} diff --git a/compiler/src/dmd/backend/arm/disasmarm.d b/compiler/src/dmd/backend/arm/disasmarm.d new file mode 100644 index 000000000000..cd3914b13931 --- /dev/null +++ b/compiler/src/dmd/backend/arm/disasmarm.d @@ -0,0 +1,2035 @@ +/********************************************************* + * ARM64 disassembler. + * For unit tests: dmd disasmarm.d -unittest -main + * + * Copyright: Copyright (C) 1982-1998 by Symantec + * Copyright (C) 2000-2024 by The D Language Foundation, All Rights Reserved + * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) + * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) + * Reference: Arm A64 Instruction Set for A-profile Architecture ISA_A64_xml_A_profile-2024-03.pdf + * A64 instruction set https://www.scs.stanford.edu/~zyedidia/arm64/ + */ + +module dmd.backend.arm.disasmarm; + +nothrow @nogc: + +/***************************** + * Calculate and return the number of bytes in an instruction starting at code[c]. + * Params: + * code = machine code as array of bytes + * c = address of instruction (as index into code[]) + * pc = set to address of instruction after prefix + * model = memory model, 16/32/64 + */ + +public +addr calccodsize(ubyte[] code, addr c, out addr pc, uint model) +{ + assert(model == 64); + Disasm disasm = Disasm(code, model); + return disasm.calccodsize(c, pc); +} + +/************************ + * If instruction is a jump or a call, get information about + * where the offset is and what it is. + * Params: + * code = instruction bytes + * c = address of start of instruction, not including prefix. + * Use calccodsize() to determine start not including prefix. + * Updated to be address of the offset part of the instruction. + * Caller determines if it is relative to the start of the next + * instruction or not. + * offset = set to be address of jump target + * Returns: + * true if jump or call target + */ +@trusted +public +bool jmpTarget(ubyte[] code, ref addr c, out addr offset) +{ +static if (0) +{ + const op = code[c] & 0xFF; + if (inssize[op] & B) // byte jump + { + ++c; + offset = cast(byte) code[c]; + } + else if (inssize[op] & W) // word jump + { + ++c; + offset = cast(short)((code[c] & 0xFF) + (code[c + 1] << 8)); + } + else if (op == 0x0F && inssize2[code[c + 1]] & W) // word/dword jump + { + c += 2; + /* BUG: look only at 16 bits of offset */ + offset = cast(short)((code[c] & 0xFF) + (code[c + 1] << 8)); + } + else + return false; +} + return true; +} + +/************************* + * Write to put() the disassembled instruction + * Params: + * put = function to write the output string to + * code = instruction bytes + * c = address (index into code[]) of start of instruction to disassemble + * siz = number of bytes in instruction (from calccodsize()) + * model = memory model, 16/32/64 + * nearptr = use 'near ptr' when writing memory references + * bObjectcode = also prepend hex characters of object code + * mem = if not null, then function that returns a string + * representing the label for the memory address. Parameters are `c` + * for the address of the memory reference in `code[]`, `sz` for the + * number of bytes in the referred to memory location, and `offset` + * for the value to be added to any symbol referenced. + * immed16 = if not null, then function that returns a string + * representation of immediate value. + * Parameters are `code` is the binary instructions, + * `c` is the address of the memory reference in `code[]`, + * `sz` is the number of bytes in the instruction that form the referenece (2/4/8) + * labelcode = if not null, then function that returns a string + * representation of code label. + * Parameters are + * `c` is the address of the code reference to the label in `code[]`, + * `offset` is the address of the label in `code[]`, + * `farflag` is if `far` reference (seg:offset in 16 bit code), + * `is16bit` is if 16 bit reference + * shortlabel = if not null, then function that returns a string + * representing the label for the target. Parameters are `pc` + * for the program counter value, and `offset` for the offset + * of the label from the pc. + */ +@trusted +public +void getopstring(void delegate(char) nothrow @nogc put, ubyte[] code, uint c, addr siz, + uint model, int nearptr, ubyte bObjectcode, + const(char)*function(uint c, uint sz, uint offset) nothrow @nogc mem, + const(char)*function(ubyte[] code, uint c, int sz) nothrow @nogc immed16, + const(char)*function(uint c, uint offset, bool farflag, bool is16bit) nothrow @nogc labelcode, + const(char)*function(uint pc, int offset) nothrow @nogc shortlabel + ) +{ + assert(model == 64); + auto disasm = Disasm(put, code, siz, + model, nearptr, bObjectcode, + mem, immed16, labelcode, shortlabel); + disasm.disassemble(c); +} + +/************************************************************************************/ +private: + + +import core.stdc.stdio; +import core.stdc.string; + +alias addr = uint; +alias addr64 = ulong; + +enum BUFMAX = 2000; + +/*********************************************** + * The disassembler + */ + +struct Disasm +{ + nothrow @nogc: + + @trusted + this(void delegate(char) nothrow @nogc put, ubyte[] code, addr siz, + uint model, int nearptr, ubyte bObjectcode, + const(char)*function(uint c, uint sz, uint offset) nothrow @nogc mem, + const(char)*function(ubyte[] code, uint c, int sz) nothrow @nogc immed16, + const(char)*function(uint c, uint offset, bool farflag, bool is16bit) nothrow @nogc labelcode, + const(char)*function(uint pc, int offset) nothrow @nogc shortlabel + ) + { + this.put = put; + this.code = code; + this.siz = siz; + this.model = model; + this.nearptr = nearptr; + this.bObjectcode = bObjectcode; + + /* Set null function pointers to default functions + */ + this.mem = mem ? mem : &memoryDefault; + this.immed16 = immed16 ? immed16 : &immed16Default; + this.labelcode = labelcode ? labelcode : &labelcodeDefault; + this.shortlabel = shortlabel ? shortlabel : &shortlabelDefault; + + defopsize = model == 16; + defadsize = model == 32 || model == 64; + + // Reset globals + opsize = defopsize; + adsize = defadsize; + fwait = 0; + segover = "".ptr; + } + + /* Enough to get prefixbyte() working + */ + this(ubyte[] code, uint model) + { + this.code = code; + this.model = model; + defopsize = model == 16; + defadsize = model == 32 || model == 64; + opsize = defopsize; + adsize = defadsize; + fwait = 0; + segover = "".ptr; + } + + ubyte[] code; // the code segment contents + void delegate(char) put; + addr siz; + int nearptr; + ubyte bObjectcode; + bool defopsize; // default value for opsize + char defadsize; // default value for adsize + bool opsize; // if 0, then 32 bit operand + char adsize; // if !=0, then 32 or 64 bit address + char fwait; // if !=0, then saw an FWAIT + uint model; // 16/32/64 + const(char)* segover; // segment override string + + // Callbacks provided by caller + const(char)*function(uint c, uint sz, addr offset) mem; + const(char)*function(ubyte[] code, uint c, int sz) immed16; + const(char)*function(uint c, uint offset, bool farflag, bool is16bit) labelcode; + const(char)*function(uint pc, int offset) shortlabel; + + +addr calccodsize(addr c, out addr pc) +{ + pc = c; + return 4; +} + +/***************************** + * Load byte at code[c]. + */ + +const(char)* immed8(uint c) @safe +{ + return wordtostring(code[c]); +} + +/***************************** + * Load byte at code[c], and sign-extend it + */ + +const(char)* immeds(uint c) +{ + return wordtostring(cast(byte) code[c]); +} + + + + +void puts(const(char)* s) +{ + while (*s) + { + put(*s); + ++s; + } +} + +/************************* + * Disassemble the instruction at `c` + * Params: + * c = index into code[] + */ + +void disassemble(uint c) +{ + //printf("disassemble(c = %d, siz = %d)\n", c, siz); + enum log = false; + puts(" "); + + int i; + char[80] p0; + const(char)* sep; + const(char)* s2; + const(char)* s3; + char[BUFMAX] buf = void; + + buf[0] = 0; + sep = ",".ptr; + s2 = "".ptr; + s3 = s2; + uint ins = *(cast(uint*)&code[c]); + p0[0]='\0'; + + if (bObjectcode) { + for (i=siz; i; --i) { + snprintf( buf.ptr, buf.length, "%02X", code[c+i-1] ); + printf("%s ", buf.ptr); + //strcat( p0.ptr, buf.ptr ); + } + } + + char[8+1] p1buf = void; + snprintf(p1buf.ptr,p1buf.length,"%08x", ins); + if (log) printf("ins: %s %d %d\n", p1buf.ptr, field(ins, 28, 24), field(ins, 21, 21)); + const(char)* p1 = p1buf.ptr; + const(char)* p2 = ""; + const(char)* p3 = ""; + const(char)* p4 = ""; + const(char)* p5 = ""; + const(char)* p6 = ""; + const(char)* p7 = ""; + + immutable char*[4] addsubTab = [ "add", "adds", "sub", "subs" ]; + immutable char*[16] condstring = + [ "eq", "ne", "cs", "cc", "mi", "pl", "vs", "vc", "hi", "ls", "ge", "lt", "gt", "le", "av", "nv" ]; + + void shiftP() + { + p2 = p3; + p3 = p4; + p4 = p5; + p5 = p6; + p6 = p7; + p7 = ""; + } + + if (field(ins, 31, 16) == 0) // http://www.scs.stanford.edu/~zyedidia/arm64/encodingindex.html#reserved + { + if (log) printf("reserved"); + uint imm16 = field(ins, 15, 0); + p1 = "udf"; + p2 = wordtostring(imm16); + } + else if (field(ins, 30, 23) == 0xE7) // http://www.scs.stanford.edu/~zyedidia/arm64/encodingindex.html#dp_1src_imm + { + if (log) printf("Data-processing (1 source immediate)\n"); + uint sf = field(ins, 31, 31); + uint opc = field(ins, 22, 21); + uint imm16 = field(ins, 20, 05); + uint Rd = field(ins, 4, 0); + + if (sf == 1 && (opc & 2) == 0 && Rd == 0x1F) + { + p1 = opc & 1 ? "aitobsppc" : "aitoasppc"; + p2 = wordtostring(imm16); + } + } + else if (field(ins, 28, 23) == 0x10) // http://www.scs.stanford.edu/~zyedidia/arm64/encodingindex.html#pcreladdr + { + if (log) printf("PC-rel. addressing\n"); + uint op = field(ins, 31, 31); + uint immlo = field(ins, 30, 29); + uint immhi = field(ins, 23, 05); + uint Rd = field(ins, 4, 0); + + p1 = op ? "adrp" : "adr"; + p2 = regString(1, Rd); + uint imm = op ? ((immhi << 2) | immlo) << 12 + : ((immhi << 2) | immlo); + p3 = wordtostring(imm); + } + else if (field(ins, 28, 23) == 0x22) // https://www.scs.stanford.edu/~zyedidia/arm64/encodingindex.html#addsub_ext + { + if (log) printf("Add/subtract (immediate)\n"); + uint sf = field(ins, 31, 31); + uint op = field(ins, 30, 30); + uint S = field(ins, 29, 29); + uint sh = field(ins, 22, 22); + uint imm12 = field(ins, 21, 10); + uint Rn = field(ins, 9, 5); + uint Rd = field(ins, 4, 0); + + uint opS = op * 2 + S; + + p1 = addsubTab[opS]; + p2 = regString(sf, Rd); + p3 = regString(sf, Rn); + p4 = wordtostring(imm12 << (sh * 12)); + + if (opS == 0 && sh == 0 && imm12 == 0 && (Rd == 31 || Rn == 31)) + { + p1 = "mov"; // https://www.scs.stanford.edu/~zyedidia/arm64/add_addsub_imm.html + p4 = ""; + } + else if (opS == 1 && Rd == 31) // adds + { + p1 = "cmn"; // https://www.scs.stanford.edu/~zyedidia/arm64/adds_addsub_imm.html + shiftP(); + } + else if (opS == 3 && Rd == 31) + { + p1 = "cmp"; // https://www.scs.stanford.edu/~zyedidia/arm64/cmp_subs_addsub_imm.html + shiftP(); + } + } + else if (field(ins, 28, 22) == 0x45) // http://www.scs.stanford.edu/~zyedidia/arm64/encodingindex.html#pcreladdr + { + if (log) printf("Add/subtract (immediate, with tags)\n"); + uint sf = field(ins, 31, 31); + uint op = field(ins, 30, 30); + uint S = field(ins, 29, 29); + uint uimm6 = field(ins, 21, 16); + uint op3 = field(ins, 15, 14); + uint uimm4 = field(ins, 13, 10); + uint Rn = field(ins, 9, 5); + uint Rd = field(ins, 4, 0); + + if (sf == 1 && S == 0) + { + p1 = op ? "subg" : "addg"; + p2 = regString(sf, Rd); + p3 = regString(sf, Rn); + p4 = wordtostring(uimm6); + p5 = wordtostring(uimm4); + } + } + else if (field(ins, 28, 22) == 0x47) // http://www.scs.stanford.edu/~zyedidia/arm64/encodingindex.html#minmax_imm + { + if (log) printf("Min/max (immediate)\n"); + uint sf = field(ins, 31, 31); + uint op = field(ins, 30, 30); + uint S = field(ins, 29, 29); + uint opc = field(ins, 21, 18); + uint imm8 = field(ins, 17, 10); + uint Rn = field(ins, 9, 5); + uint Rd = field(ins, 4, 0); + + if (op == 0 && S == 0 && opc < 4) + { + immutable char*[4] opstring = [ "smax", "umax", "smin", "umin" ]; + p1 = opstring[opc]; + p2 = regString(sf, Rd); + p3 = regString(sf, Rn); + p4 = wordtostring(imm8); + } + } + else if (field(ins, 28, 23) == 0x24) // http://www.scs.stanford.edu/~zyedidia/arm64/encodingindex.html#log_imm + { + if (log) printf("Logical (immediate)\n"); + uint sf = field(ins, 31, 31); + uint opc = field(ins, 30, 29); + uint N = field(ins, 22, 22); + uint immr = field(ins, 21, 16); + uint imms = field(ins, 15, 10); + uint Rn = field(ins, 9, 5); + uint Rd = field(ins, 4, 0); + if (sf || N == 0) + { + immutable char*[4] opstring = [ "and", "orr", "eor", "ands" ]; + p1 = opstring[opc]; + p2 = regString(sf, Rd); + p3 = regString(sf, Rn); + uint imm = sf ? (imms << 6) | immr : (N << 12) | (imms << 6) | immr; + p4 = wordtostring(imm); + if (opc == 3 && Rd == 0x2F) + { + p1 = "tst"; + shiftP(); + } + } + } + else if (field(ins, 28, 23) == 0x25) // https://www.scs.stanford.edu/~zyedidia/arm64/encodingindex.html#movewidex + { + if (log) printf("Move wide (immediate)\n"); + uint sf = field(ins, 31, 31); + uint opc = field(ins, 30, 29); + uint hw = field(ins, 22, 21); + uint imm16 = field(ins, 20, 5); + uint Rd = field(ins, 4, 0); + if (opc == 0) // https://www.scs.stanford.edu/~zyedidia/arm64/movn.html + { + bool mov = !(imm16 == 0 && hw != 0) && imm16 != 0xFFFF; + p1 = mov ? "mov" : "movn"; + ulong imm = imm16 << (hw * 16); + if (mov) + imm = ~imm; + p3 = wordtostring(imm); + } + else if (opc == 2) + { + p1 = (imm16 || hw == 0) ? "mov" : "movz"; + p3 = wordtostring(imm16 << (hw * 16)); + } + else if (opc == 3) + { + p1 = "movk"; + p3 = wordtostring(imm16 << (hw * 16)); + } + p2 = regString(sf, Rd); + } + else if (field(ins, 28, 23) == 0x26) // http://www.scs.stanford.edu/~zyedidia/arm64/encodingindex.html#bitfield + { + if (log) printf("Bitfield\n"); + uint sf = field(ins, 31, 31); + uint opc = field(ins, 30, 29); + uint N = field(ins, 22, 22); + uint immr = field(ins, 21, 16); + uint imms = field(ins, 15, 10); + uint Rn = field(ins, 9, 5); + uint Rd = field(ins, 4, 0); + + p2 = regString(sf, Rd); + p3 = regString(sf, Rn); + + if (opc == 0) // SBFM + { + if ((sf ? 63 : 31) == imms) + { + p1 = "asr"; + p4 = wordtostring(immr); + } + else if (imms < immr) + { + p1 = "sbfiz"; // https://www.scs.stanford.edu/~zyedidia/arm64/sbfiz_sbfm.html + uint lsb = sf ? (-immr & 63) : (-immr & 31); + uint width = imms + 1; + p4 = wordtostring(lsb); // is this right? + p5 = wordtostring2(width); + } + else if (immr == 0 && imms == 7) + { + p1 = "sxtb"; + } + else if (immr == 0 && imms == 15) + { + p1 = "sxth"; + } + else if (immr == 0 && imms == 31) + { + p1 = "sxtw"; + } + else if (1) // https://www.scs.stanford.edu/~zyedidia/arm64/sbfx_sbfm.html + { + p1 = "sbfx"; + p4 = wordtostring(-immr); + p5 = wordtostring2(imms + 1 - immr); + } + else + { + p1 = "sbfm"; // https://www.scs.stanford.edu/~zyedidia/arm64/sbfm.html + p4 = wordtostring(immr); + p5 = wordtostring2(imms); + } + } + else if (opc == 1) // BFM https://www.scs.stanford.edu/~zyedidia/arm64/bfm.html + { + if (Rn == 0x1F && imms < immr) + { + p1 = "bfc"; // https://www.scs.stanford.edu/~zyedidia/arm64/bfc.html + p3 = wordtostring(-immr); + p4 = wordtostring2(imms - 1); + } + else if (Rn != 0x1F && imms < immr) + { + p1 = "bfi"; // https://www.scs.stanford.edu/~zyedidia/arm64/bfi.html + p4 = wordtostring(-immr); + p5 = wordtostring2(imms - 1); + } + else if (imms >= immr) + { + p1 = "bfxil"; // https://www.scs.stanford.edu/~zyedidia/arm64/bfxil.html + p4 = wordtostring(immr); + p5 = wordtostring2(imms + 1 - immr); + } + else + { + p1 = "bfm"; // https://www.scs.stanford.edu/~zyedidia/arm64/bfm.html + p4 = wordtostring(immr); + p5 = wordtostring2(imms); + } + } + else if (opc == 2) // UBFM + { + if ((sf ? imms != 31 : imms != 15) && imms + 1 == immr) + { + p1 = "lsl"; // https://www.scs.stanford.edu/~zyedidia/arm64/lsl_ubfm.html + p4 = wordtostring((sf ? 63 : 31) - imms); + } + else if (sf ? imms == 63 : imms == 31) + { + p1 = "lsr"; // https://www.scs.stanford.edu/~zyedidia/arm64/lsr_ubfm.html + p4 = wordtostring(immr); + } + else if (imms < immr) + { + p1 = "ubfiz"; // https://www.scs.stanford.edu/~zyedidia/arm64/ubfiz_ubfm.html + p4 = wordtostring(-immr); + p5 = wordtostring2(imms - 1); + } + else if (immr == 0 && imms == 7) + { + p1 = "uxtb"; // https://www.scs.stanford.edu/~zyedidia/arm64/uxtb_ubfm.html + } + else if (immr == 0 && imms == 15) + { + p1 = "uxth"; // https://www.scs.stanford.edu/~zyedidia/arm64/uxth_ubfm.html + } + else if (1) + { + p1 = "ubfx"; // https://www.scs.stanford.edu/~zyedidia/arm64/ubfx_ubfm.html + p4 = wordtostring(immr); + p5 = wordtostring2(imms + 1 - immr); + } + else + { + p1 = "ubfm"; // https://www.scs.stanford.edu/~zyedidia/arm64/ubfm.html + p4 = wordtostring(immr); + p5 = wordtostring2(imms); + } + } + } + else if (field(ins, 28, 23) == 0x27) // http://www.scs.stanford.edu/~zyedidia/arm64/encodingindex.html#extract + { + if (log) printf("Extract\n"); + uint sf = field(ins, 31, 31); + uint op21 = field(ins, 30, 29); + uint N = field(ins, 22, 22); + uint oO = field(ins, 21, 21); + uint Rm = field(ins, 20, 16); + uint imms = field(ins, 15, 10); + uint Rn = field(ins, 9, 5); + uint Rd = field(ins, 4, 0); + if (Rn == Rm) + { + p1 = "ror"; + p2 = regString(sf, Rd); + p3 = regString(sf, Rn); + p4 = wordtostring(imms); + } + else + { + p1 = "extr"; + p2 = regString(sf, Rd); + p3 = regString(sf, Rn); + p4 = regString(sf, Rm); + p5 = wordtostring(imms); + } + } + else if (field(ins, 31, 24) == 0x54) // http://www.scs.stanford.edu/~zyedidia/arm64/encodingindex.html#condbranch + { + if (log) printf("Conditional branch (immediate)\n"); + uint imm19 = field(ins, 23, 5); + uint oO = field(ins, 4, 4); + uint cond = field(ins, 3, 0); + + const char* format = oO ? "BC.%s" : "B.%s"; + sprintf(buf.ptr, format, condstring[cond]); + p1 = buf.ptr; + p2 = wordtostring(imm19); + } + else if (field(ins, 31, 24) == 0x55) // http://www.scs.stanford.edu/~zyedidia/arm64/encodingindex.html#miscbranch + { + if (log) printf("Miscellaneous branch (immediate)\n"); + uint opc = field(ins, 23, 21); + uint imm16 = field(ins, 20, 5); + uint op2 = field(ins, 4, 0); + + if ((opc & 6) == 0 && op2 == 0x1F) + { + p1 = opc ? "retabsppc" : "retaaspcc"; + p2 = labeltostring(imm16 << 2); + } + } + else if (field(ins, 31, 24) == 0xB4) // http://www.scs.stanford.edu/~zyedidia/arm64/encodingindex.html#exception + { + if (log) printf("Exception generation\n"); + uint opc = field(ins, 23, 21); + uint imm16 = field(ins, 20, 5); + uint op2 = field(ins, 4, 2); + uint LL = field(ins, 1, 0); + + if (op2 == 0) + { + uint X(uint opc, uint LL) { return (opc << 2) | LL; } + switch (X(opc, LL)) + { + case X(0, 1): p1 = "svc"; goto Limm; + case X(0, 2): p1 = "hvc"; goto Limm; + case X(0, 3): p1 = "smc"; goto Limm; + case X(1, 0): p1 = "brk"; goto Limm; + case X(2, 0): p1 = "hlt"; goto Limm; + case X(3, 0): p1 = "tcancel"; goto Limm; + Limm: + p2 = wordtostring(imm16); + break; + + case X(5, 1): p1 = "dcps1"; goto Ldcps; + case X(5, 2): p1 = "dcps2"; goto Ldcps; + case X(5, 3): p1 = "dcps3"; goto Ldcps; + Ldcps: + if (imm16) + p2 = wordtostring(imm16); + break; + + default: + break; + } + } + } + else if (field(ins, 31, 12) == 0xD5031) // http://www.scs.stanford.edu/~zyedidia/arm64/encodingindex.html#systeminstrswithreg + { + if (log) printf("System instructions with register argument\n"); + uint CRm = field(ins, 11, 8); + uint op2 = field(ins, 7, 5); + uint Rt = field(ins, 4, 0); + + if (CRm == 0 && (op2 >> 1) == 0) + { + p1 = op2 ? "wfit" : "wfet"; + p2 = regString(1, Rt); + } + } + else if (field(ins, 31, 12) == 0xD5032) // http://www.scs.stanford.edu/~zyedidia/arm64/encodingindex.html#hints + { + if (log) printf("Hints\n"); + uint CRm = field(ins, 11, 8); + uint op2 = field(ins, 7, 5); + uint Rt = field(ins, 4, 0); + + if (CRm <= 5 && Rt == 0x1F) + { + uint Z(uint CRm, uint op2) { return (CRm << 3) | op2; } + switch (Z(CRm, op2)) + { + case Z(0, 0): p1 = "nop"; break; + case Z(0, 1): p1 = "yield"; break; + case Z(0, 2): p1 = "wfe"; break; + case Z(0, 3): p1 = "wfi"; break; + case Z(0, 4): p1 = "sev"; break; + case Z(0, 5): p1 = "sevl"; break; + case Z(0, 6): p1 = "dgh"; break; + case Z(0, 7): p1 = "xpaclri"; break; + case Z(1, 0): p1 = "pacia1716"; break; + case Z(1, 2): p1 = "pacob1716"; break; + case Z(1, 4): p1 = "autia1716"; break; + case Z(1, 6): p1 = "autib1716"; break; + case Z(2, 0): p1 = "esb"; break; + case Z(2, 1): p1 = "psb csync"; break; + case Z(2, 2): p1 = "tsb csync"; break; + case Z(2, 3): p1 = "gcsb dsync"; break; + case Z(2, 4): p1 = "csdb"; break; + case Z(2, 6): p1 = "clrbhb"; break; + case Z(3, 0): p1 = "paciaz"; break; + case Z(3, 1): p1 = "paciasp"; break; + case Z(3, 2): p1 = "pacibz"; break; + case Z(3, 3): p1 = "pacibsp"; break; + case Z(3, 4): p1 = "autiaz"; break; + case Z(3, 5): p1 = "autiasp"; break; + case Z(3, 6): p1 = "autibz"; break; + case Z(3, 7): p1 = "autibsp"; break; + case Z(4, 7): p1 = "pacm"; break; + case Z(5, 0): p1 = "chkfeat x16"; break; + + default: + if (CRm == 4 && (op2 & 1) == 0) + { + p1 = "bti"; + immutable char*[4] option = [ "", "c", "j", "jc" ]; + p2 = option[op2 >> 1]; + } + break; + } + } + } + else if (field(ins, 31, 12) == 0xD5033) // http://www.scs.stanford.edu/~zyedidia/arm64/encodingindex.html#barriers + { + if (log) printf("Barriers\n"); + uint CRm = field(ins, 11, 8); + uint op2 = field(ins, 7, 5); + uint Rt = field(ins, 4, 0); + + if (Rt == 0x1F) + { + if (op2 == 2) + { + p1 = "clrex"; + if (CRm) + p2 = wordtostring(CRm); + } + else if (op2 == 4) + { + if (CRm == 4) + p1 = "pssbb"; + else if (CRm == 0) + p1 = "ssbb"; + else + { + p1 = "dsb"; + p2 = wordtostring(CRm); + } + } + else if (op2 == 5) + { + p1 = "dsb"; + p2 = wordtostring(CRm); + } + else if (op2 == 6) + { + p1 = "isb"; + if (CRm != 15) + p2 = wordtostring(CRm); + } + else if (op2 == 7) + p1 = "sb"; + else if ((CRm & 3) == 2 && op2 == 1) + { + p1 = "dsb"; + immutable char*[4] xs = [ "oshnXS", "nshnXS", "ishnXS", "synXS" ]; + p2 = xs[CRm >> 2]; + } + else if (CRm == 0 && op2 == 3) + p1 = "tcommit"; + } + } + else if (field(ins, 31, 19) == 0x1AA0) // http://www.scs.stanford.edu/~zyedidia/arm64/encodingindex.html#pstate + { + if (log) printf("PSTATE\n"); + uint op1 = field(ins, 18, 16); + uint CRm = field(ins, 11, 8); + uint op2 = field(ins, 7, 5); + uint Rt = field(ins, 4, 0); + + if (Rt == 0x1F) + { + if (op1 == 0 && op2 == 0) + p1 = "cfinv"; + else if (op1 == 0 && op2 == 1) + p1 = "xaflag"; + else if (op1 == 0 && op2 == 2) + p1 = "axflag"; + else + { + p1 = "msr"; + p2 = wordtostring((op1 << 7) | (op2 << 4) | CRm); // + p3 = wordtostring(CRm); + } + } + } + else if (field(ins, 31, 19) == 0x1AA4) // http://www.scs.stanford.edu/~zyedidia/arm64/encodingindex.html#systemresult + { + if (log) printf("System with result\n"); + uint op1 = field(ins, 18, 16); + uint CRn = field(ins, 15, 12); + uint CRm = field(ins, 11, 8); + uint op2 = field(ins, 7, 5); + uint Rt = field(ins, 4, 0); + + if (op1 == 3 && CRn == 3 && op2 == 3) + { + if (CRm == 0) + { + p1 = "tstart"; + p2 = regString(1, Rt); + } + else if (CRm == 1) + { + p1 = "ttest"; + p2 = regString(1, Rt); + } + } + } + else if (field(ins, 31, 22) == 0x354 && field(ins, 20, 19) == 1) // http://www.scs.stanford.edu/~zyedidia/arm64/encodingindex.html#systeminstrs + { + if (log) printf("System instructions\n"); + uint L = field(ins, 21, 21); + uint op1 = field(ins, 18, 16); + uint CRn = field(ins, 15, 12); + uint CRm = field(ins, 11, 8); + uint op2 = field(ins, 7, 5); + uint Rt = field(ins, 4, 0); + + if (L) + { + if (op1 == 3 && CRn == 7 && CRm == 7 && (op2 == 1 || op2 == 3)) + { + p1 = op2 == 1 ? "gcspopm" : "gcss2"; + if (Rt != 0x1F || op2 == 3) + p2 = regString(1, Rt); + } + else + { + p1 = "sysl"; + p2 = regString(1, Rt); + p3 = wordtostring(op1); + p4 = cregString(CRn); + p5 = cregString(CRm); + p6 = wordtostring2(op2); + } + } + else + { + p1 = "sys"; + p2 = wordtostring(op1); + p3 = cregString(CRn); + p4 = cregString(CRm); + p5 = wordtostring2(op2); + if (Rt != 0x1F) + p6 = regString(1, Rt); + // TODO: a bunch of aliases http://www.scs.stanford.edu/~zyedidia/arm64/sys.html + } + } + else if (field(ins, 31, 22) == 0x354 && field(ins, 20, 20) == 1) // http://www.scs.stanford.edu/~zyedidia/arm64/encodingindex.html#systemmove + { + if (log) printf("System register move\n"); + uint L = field(ins, 21, 21); + uint oO = field(ins, 19, 19); + uint op1 = field(ins, 18, 16); + uint CRn = field(ins, 15, 12); + uint CRm = field(ins, 11, 8); + uint op2 = field(ins, 7, 5); + uint Rt = field(ins, 4, 0); + + snprintf(buf.ptr, cast(uint)buf.length, "S%d_%d_%s_%s_%d".ptr, oO + 2, op1, cregString(CRn), cregString(CRm), op2); + if (L) + { + p1 = "mrs"; + p2 = regString(1, Rt); + p3 = buf.ptr; + } + else + { + p1 = "msr"; + p2 = buf.ptr; + p3 = regString(1, Rt); + } + } + else if (field(ins, 31, 22) == 0x355 && field(ins, 20, 19) == 1) // http://www.scs.stanford.edu/~zyedidia/arm64/encodingindex.html#syspairinstrs + { + if (log) printf("System pair instructions\n"); + uint L = field(ins, 21, 21); + uint op1 = field(ins, 18, 16); + uint CRn = field(ins, 15, 12); + uint CRm = field(ins, 11, 8); + uint op2 = field(ins, 7, 5); + uint Rt = field(ins, 4, 0); + + if (L == 0) + { + p1 = "sysp"; + p2 = wordtostring(op1); + p3 = cregString(CRn); + p4 = cregString(CRm); + p5 = wordtostring2(op2); + if (Rt != 0x1F) + { + p6 = regString(1, Rt); + p7 = regString(1, Rt + 1); + } + // TODO: tlbip alias http://www.scs.stanford.edu/~zyedidia/arm64/tlbip_sysp.html + } + } + else if (field(ins, 31, 22) == 0x355 && field(ins, 20, 20) == 1) // http://www.scs.stanford.edu/~zyedidia/arm64/encodingindex.html#systemmovepr + { + if (log) printf("System register pair move\n"); + uint L = field(ins, 21, 21); + uint oO = field(ins, 19, 19); + uint op1 = field(ins, 18, 16); + uint CRn = field(ins, 15, 12); + uint CRm = field(ins, 11, 8); + uint op2 = field(ins, 7, 5); + uint Rt = field(ins, 4, 0); + + snprintf(buf.ptr, cast(uint)buf.length, "S%d_%d_%s_%s_%d".ptr, oO + 2, op1, cregString(CRn), cregString(CRm), op2); + if (L) + { + p1 = "mrrs"; + p2 = regString(1, Rt); + p3 = regString(1, Rt + 1); + p4 = buf.ptr; + } + else + { + p1 = "msrr"; + p2 = buf.ptr; + p3 = regString(1, Rt); + p4 = regString(1, Rt + 1); + } + } + else if (field(ins, 31, 25) == 0x6B) // http://www.scs.stanford.edu/~zyedidia/arm64/encodingindex.html#branch_reg + { + if (log) printf("Unconditional branch (register)\n"); + uint opc = field(ins, 24, 21); + uint op2 = field(ins, 20, 16); + uint op3 = field(ins, 15, 10); + uint Rn = field(ins, 9, 5); + uint op4 = field(ins, 4, 0); + + //printf("opc x%0x op2 x%0x op3 x%0x Rn x%0x op4 x%0x\n", opc, op2, op3, Rn, op4); + if (opc == 0 && op2 == 0x1F && op3 == 0 && op4 == 0) + { + p1 = "br"; + p2 = xregs[Rn]; + } + else if (opc == 0 && op2 == 0x1F && op3 == 2 && op4 == 0x1F) + { + p1 = "braaz"; + p2 = xregs[Rn]; + } + else if (opc == 0 && op2 == 0x1F && op3 == 3 && op4 == 0x1F) + { + p1 = "brabz"; + p2 = xregs[Rn]; + } + else if (opc == 1 && op2 == 0x1F && op3 == 0 && op4 == 0) + { + p1 = "blr"; + p2 = xregs[Rn]; + } + else if (opc == 1 && op2 == 0x1F && op3 == 2 && op4 == 0x1F) + { + p1 = "blraaz"; + p2 = xregs[Rn]; + } + else if (opc == 1 && op2 == 0x1F && op3 == 3 && op4 == 0x1F) + { + p1 = "blrabz"; + p2 = xregs[Rn]; + } + else if (opc == 2 && op2 == 0x1F && op3 == 0 && op4 == 0) + { + p1 = "ret"; + if (Rn != 30) + p2 = xregs[Rn]; + } + else if (opc == 2 && op2 == 0x1F && op3 == 2 && Rn == 0x1F && op4 == 0x1F) + p1 = "retaa"; + else if (opc == 2 && op2 == 0x1F && op3 == 3 && Rn == 0x1F && op4 == 0x1F) + p1 = "retab"; + else if (opc == 2 && op2 == 0x1F && op3 == 2 && Rn == 0x1F && op4 != 0x1F) + { + p1 = "retaasppc"; + p2 = xregs[op4]; + } + else if (opc == 2 && op2 == 0x1F && op3 == 3 && Rn == 0x1F && op4 != 0x1F) + { + p1 = "retabsppc"; + p2 = xregs[op4]; + } + else if (opc == 4 && op2 == 0x1F && op3 == 0 && Rn == 0x1F && op4 == 0) + p1 = "eret"; + else if (opc == 4 && op2 == 0x1F && op3 == 2 && Rn == 0x1F && op4 == 0x1F) + p1 = "eretaa"; + else if (opc == 4 && op2 == 0x1F && op3 == 3 && Rn == 0x1F && op4 == 0x1F) + p1 = "eretab"; + else if (opc == 5 && op2 == 0x1F && op3 == 0 && Rn == 0x1F && op4 == 0) + p1 = "drps"; + else if (opc == 0x8 && op2 == 0x1F && op3 == 2) + { + p1 = "braa"; + p2 = xregs[Rn]; + if (op4 != 0x1F) + p3 = xregs[op4]; + } + else if (opc == 0x8 && op2 == 0x1F && op3 == 3) + { + p1 = "brab"; + p2 = xregs[Rn]; + if (op4 != 0x1F) + p3 = xregs[op4]; + } + else if (opc == 0x9 && op2 == 0x1F && op3 == 2) + { + p1 = "blraa"; + p2 = xregs[Rn]; + if (op4 != 0x1F) + p3 = xregs[op4]; + } + else if (opc == 0x9 && op2 == 0x1F && op3 == 3) + { + p1 = "blrab"; + p2 = xregs[Rn]; + if (op4 != 0x1F) + p3 = xregs[op4]; + } + } + else if (field(ins, 30, 26) == 0x05) // http://www.scs.stanford.edu/~zyedidia/arm64/encodingindex.html#branch_imm + { + if (log) printf("Unconditional branch (immediate)\n"); + uint op = field(ins, 31, 31); + uint imm26 = field(ins, 25, 0); + + p1 = op ? "bl" : "b"; + p2 = wordtostring(imm26 * 4); + } + else if (field(ins, 30, 25) == 0x1A) // http://www.scs.stanford.edu/~zyedidia/arm64/encodingindex.html#compbranch + { + if (log) printf("Compare and branch (immediate)\n"); + uint sf = field(ins, 31, 31); + uint op = field(ins, 24, 24); + uint imm19 = field(ins, 23, 5); + uint Rt = field(ins, 4, 0); + + p1 = op ? "cbnz" : "cbz"; + p2 = regString(sf, Rt); + p3 = wordtostring(imm19 * 4); + } + else if (field(ins, 30, 25) == 0x1B) // http://www.scs.stanford.edu/~zyedidia/arm64/encodingindex.html#testbranch + { + if (log) printf("Test and branch (immediate)\n"); + uint b5 = field(ins, 31, 31); + uint op = field(ins, 24, 24); + uint b40 = field(ins, 23, 19); + uint imm14 = field(ins, 18, 5); + uint Rt = field(ins, 4, 0); + + p1 = op ? "tbnz" : "tbz"; + p2 = regString(b5, Rt); + p3 = wordtostring((b5 << 5) | b40); + p4 = wordtostring(imm14 * 4); + } + else if (field(ins, 30, 30) == 0 && field(ins, 28, 21) == 0xD6) // https://www.scs.stanford.edu/~zyedidia/arm64/encodingindex.html#dp_2src + { + if (log) printf("Data-processing (2 source)\n"); + uint sf = field(ins, 31, 31); + uint S = field(ins, 29, 29); + uint Rm = field(ins, 20, 16); + uint opcode = field(ins, 15, 10); + uint Rn = field(ins, 9, 5); + uint Rd = field(ins, 4, 0); + + p2 = regString(sf, Rd); + p3 = regString(sf, Rn); + p4 = regString(sf, Rm); + + uint sfSopcode = (sf << 7) | (S << 6) | opcode; + + switch (sfSopcode) + { + case 2: // https://www.scs.stanford.edu/~zyedidia/arm64/udiv.html + case 0x82: + p1 = "udiv"; + break; + + case 3: // https://www.scs.stanford.edu/~zyedidia/arm64/sdiv.html + case 0x83: + p1 = "sdiv"; + break; + + case 8: // https://www.scs.stanford.edu/~zyedidia/arm64/lslv.html + case 0x88: + p1 = "lsl"; + break; + + case 9: // https://www.scs.stanford.edu/~zyedidia/arm64/lsrv.html + case 0x89: + p1 = "lsr"; + break; + + case 0x0A: // https://www.scs.stanford.edu/~zyedidia/arm64/asrv.html + case 0x8A: + p1 = "asr"; + break; + + case 0x0B: // https://www.scs.stanford.edu/~zyedidia/arm64/rorv.html + case 0x8B: + p1 = "ror"; + break; + + case 0x10: // https://www.scs.stanford.edu/~zyedidia/arm64/rorv.html + p1 = "crc32b"; + break; + + case 0x11: // https://www.scs.stanford.edu/~zyedidia/arm64/crc32.html#CRC32B_32C_dp_2src + p1 = "crc32h"; + break; + + case 0x12: // https://www.scs.stanford.edu/~zyedidia/arm64/rorv.html + p1 = "crc32w"; + break; + + case 0x14: // https://www.scs.stanford.edu/~zyedidia/arm64/rorv.html + p1 = "crc32cb"; + break; + + case 0x15: // https://www.scs.stanford.edu/~zyedidia/arm64/rorv.html + p1 = "crc32ch"; + break; + + case 0x16: // https://www.scs.stanford.edu/~zyedidia/arm64/rorv.html + p1 = "crc32cw"; + break; + + case 0x18: // https://www.scs.stanford.edu/~zyedidia/arm64/rorv.html + case 0x98: + p1 = "smax"; + break; + + case 0x19: // https://www.scs.stanford.edu/~zyedidia/arm64/rorv.html + case 0x99: + p1 = "umax"; + break; + + case 0x1A: // https://www.scs.stanford.edu/~zyedidia/arm64/rorv.html + case 0x9A: + p1 = "smin"; + break; + + case 0x1B: // https://www.scs.stanford.edu/~zyedidia/arm64/rorv.html + case 0x9B: + p1 = "umin"; + break; + + case 0x80: // https://www.scs.stanford.edu/~zyedidia/arm64/subp.html + p1 = "subp"; + break; + + case 0xC0: // https://www.scs.stanford.edu/~zyedidia/arm64/subps.html + p1 = "subs"; + break; + + case 0x93: // https://www.scs.stanford.edu/~zyedidia/arm64/crc32x.html + p1 = "crc32x"; + break; + + case 0x97: // https://www.scs.stanford.edu/~zyedidia/arm64/crc32cx.html + p1 = "crc32x"; + break; + + case 0x84: // https://www.scs.stanford.edu/~zyedidia/arm64/irg.html + p1 = "irg"; + break; + + case 0x85: // https://www.scs.stanford.edu/~zyedidia/arm64/gmi.html + p1 = "gmi"; + break; + + case 0x8C: // https://www.scs.stanford.edu/~zyedidia/arm64/pacga.html + p1 = "pacga"; + break; + + default: + p2 = p3 = p4 = ""; + break; + } + } + else if (field(ins, 30, 30) == 1 && field(ins, 28, 21) == 0xD6) // https://www.scs.stanford.edu/~zyedidia/arm64/encodingindex.html#dp_1src + { + if (log) printf("Data-processing (1 source)\n"); + uint sf = field(ins, 31, 31); + uint S = field(ins, 29, 29); + uint opcode2 = field(ins, 20, 16); + uint opcode = field(ins, 15, 10); + uint Rn = field(ins, 9, 5); + uint Rd = field(ins, 4, 0); + + p2 = regString(sf, Rd); + p3 = regString(sf, Rn); + + uint decode(uint sf, uint S, uint opcode2, uint opcode) + { return (sf << (1 + 5 + 6)) | (S << (5 + 6)) | (opcode2 << 6) | opcode; } + + switch (decode(sf, S, opcode2, opcode)) + { + case decode(0, 0, 0, 0x00): + case decode(1, 0, 0, 0x00): p1 = "rbit"; break; + case decode(0, 0, 0, 0x01): + case decode(1, 0, 0, 0x01): p1 = "rev16"; break; + case decode(0, 0, 0, 0x02): p1 = "rev"; break; + case decode(1, 0, 0, 0x02): p1 = "rev32"; break; + case decode(1, 0, 0, 0x03): p1 = "rev"; break; + case decode(0, 0, 0, 0x04): + case decode(1, 0, 0, 0x04): p1 = "clz"; break; + case decode(0, 0, 0, 0x05): + case decode(1, 0, 0, 0x05): p1 = "cls"; break; + case decode(0, 0, 0, 0x06): + case decode(1, 0, 0, 0x06): p1 = "ctz"; break; + case decode(0, 0, 0, 0x07): + case decode(1, 0, 0, 0x07): p1 = "cnt"; break; + case decode(0, 0, 0, 0x08): + case decode(1, 0, 0, 0x08): p1 = "abs"; break; + case decode(1, 0, 1, 0x00): p1 = "pacia"; break; + case decode(1, 0, 1, 0x01): p1 = "pacib"; break; + case decode(1, 0, 1, 0x02): p1 = "pacda"; break; + case decode(1, 0, 1, 0x03): p1 = "pacdb"; break; + case decode(1, 0, 1, 0x04): p1 = "autia"; break; + case decode(1, 0, 1, 0x05): p1 = "autib"; break; + case decode(1, 0, 1, 0x06): p1 = "autda"; break; + case decode(1, 0, 1, 0x07): p1 = "autdb"; break; + case decode(1, 0, 1, 0x08): if (Rn == 0x1F) p1 = "paciza"; break; + case decode(1, 0, 1, 0x09): if (Rn == 0x1F) p1 = "pacizb"; break; + case decode(1, 0, 1, 0x0A): if (Rn == 0x1F) p1 = "pacdza"; break; + case decode(1, 0, 1, 0x0B): if (Rn == 0x1F) p1 = "pacdzb"; break; + case decode(1, 0, 1, 0x0C): if (Rn == 0x1F) p1 = "autiza"; break; + case decode(1, 0, 1, 0x0D): if (Rn == 0x1F) p1 = "autizb"; break; + case decode(1, 0, 1, 0x0E): if (Rn == 0x1F) p1 = "autdza"; break; + case decode(1, 0, 1, 0x0F): if (Rn == 0x1F) p1 = "autdzb"; break; + case decode(1, 0, 1, 0x10): if (Rn == 0x1F) p1 = "xpaci"; break; + case decode(1, 0, 1, 0x11): if (Rn == 0x1F) p1 = "xpacd"; break; + case decode(1, 0, 1, 0x20): if (Rn == 0x1F && Rd == 0x1E) p1 = "pacnbiasppc"; break; + case decode(1, 0, 1, 0x21): if (Rn == 0x1F && Rd == 0x1E) p1 = "pacnbibsppc"; break; + case decode(1, 0, 1, 0x22): if (Rn == 0x1F && Rd == 0x1E) p1 = "pacia171615"; break; + case decode(1, 0, 1, 0x23): if (Rn == 0x1F && Rd == 0x1E) p1 = "pacib171615"; break; + case decode(1, 0, 1, 0x24): if ( Rd == 0x1E) p1 = "autiasppc"; break; + case decode(1, 0, 1, 0x25): if ( Rd == 0x1E) p1 = "autibsppc"; break; + case decode(1, 0, 1, 0x28): if (Rn == 0x1F && Rd == 0x1E) p1 = "paciasppc"; break; + case decode(1, 0, 1, 0x29): if (Rn == 0x1F && Rd == 0x1E) p1 = "pacibsppc"; break; + case decode(1, 0, 1, 0x2E): if (Rn == 0x1F && Rd == 0x1E) p1 = "autia171615"; break; + case decode(1, 0, 1, 0x2F): if (Rn == 0x1F && Rd == 0x1E) p1 = "autib171615"; break; + + default: p2 = p3 = ""; break; + } + } + else if (field(ins, 28, 24) == 0x0A) // https://www.scs.stanford.edu/~zyedidia/arm64/encodingindex.html#log_shift + { + if (log) printf("Logical (shifted register)\n"); + uint sf = field(ins, 31, 31); + uint opc = field(ins, 30, 29); + uint shift = field(ins, 23, 22); + uint N = field(ins, 21, 21); + uint Rm = field(ins, 20, 16); + uint imm6 = field(ins, 15, 10); + uint Rn = field(ins, 9, 5); + uint Rd = field(ins, 4, 0); + + immutable char*[8] opstring = [ "and", "bic", "orr", "orn", "eor", "eon", "ands", "bics" ]; + p1 = opstring[(opc << 1) | N]; + p2 = regString(sf, Rd); + p3 = regString(sf, Rn); + p4 = regString(sf, Rm); + immutable char*[4] shiftstring = [ "", "lsr ", "asr ", "ror " ]; + if (imm6) + { + __gshared char[4 + 3 + imm6.sizeof * 3 + 1 + 1] P5 = void; + snprintf(P5.ptr, P5.length, ((imm6 < 10) ? "%s #%d" : "#0x%X"), shiftstring[shift], imm6); + p5 = P5.ptr; + } + } + else if (field(ins, 28, 24) == 11 && field(ins, 21, 21) == 0) // https://www.scs.stanford.edu/~zyedidia/arm64/encodingindex.html#addsub_shift + { + if (log) printf("Add/subtract (shifted register)\n"); + uint sf = field(ins, 31, 31); + uint op = field(ins, 30, 30); + uint S = field(ins, 29, 29); + uint shift = field(ins, 23, 22); + uint Rm = field(ins, 20, 16); + uint immed6 = field(ins, 15, 10); + uint Rn = field(ins, 9, 5); + uint Rd = field(ins, 4, 0); + + uint opS = op * 2 + S; + p1 = addsubTab[opS]; + p2 = regString(sf, Rd); + p3 = regString(sf, Rn); + p4 = regString(sf, Rm); + + if (immed6) // defaults to 0 + { + immutable char*[4] tab2 = [ "lsl", "lsr", "asr", "reserved" ]; + __gshared char[1 + 8 + 1 + 3 + immed6.sizeof * 3 + 1 + 1] P5buf = void; + snprintf(P5buf.ptr, P5buf.length, ((immed6 < 10) ? "%s #%d".ptr : "#0x%X".ptr), tab2[shift], immed6); + p5 = P5buf.ptr; + } + + if (opS == 1 && Rd == 31) // adds + { + p1 = "cmn"; // https://www.scs.stanford.edu/~zyedidia/arm64/cmn_adds_addsub_shift.html + shiftP(); + } + else if (opS == 2 && Rn == 31) + { + p1 = "neg"; // https://www.scs.stanford.edu/~zyedidia/arm64/neg_sub_addsub_shift.html + p3 = p4; + p4 = p5; + p5 = ""; + } + else if (opS == 3) // subs + { + if (Rd == 31) + { + p1 = "cmp"; + shiftP(); + } + else if (Rn == 31) + { + p1 = "negs"; + shiftP(); + } + } + } + else if (field(ins, 28, 24) == 11 && field(ins, 21, 21)) // https://www.scs.stanford.edu/~zyedidia/arm64/subs_addsub_ext.html + { + if (log) printf("Add/subtract (extended register)\n"); + uint sf = field(ins, 31, 31); + uint op = field(ins, 30, 30); + uint S = field(ins, 29, 29); + uint opt = field(ins, 23, 22); + uint Rm = field(ins, 20, 16); + uint option = field(ins, 15, 13); + uint imm3 = field(ins, 12, 10); + uint Rn = field(ins, 9, 5); + uint Rd = field(ins, 4, 0); + //printf("Rd: x%x\n", Rd); + + immutable char*[8] tab = [ "uxtb", "uxth", "ustw", "uxtx", "sxtb","sxth", "sxtw", "sxtx" ]; + const(char)* extend; + if (sf && Rn == 0x1F && option == 3 || + !sf && Rn == 0x1F && option == 2) + extend = imm3 ? "lsl" : ""; + else + extend = tab[option]; + + uint opS = op * 2 + S; + p1 = addsubTab[opS]; + p2 = regString(sf, Rd); + p3 = regString(sf, Rn); + if (sf) + p4 = regString(option == 3 || option == 7, Rm); + else + p4 = regString(sf, Rm); + + __gshared char[1 + 4 + 1 + 3 + imm3.sizeof * 3 + 1 + 1] P5buf2 = void; + if (imm3 == 0) + p5 = extend; + else + snprintf(P5buf2.ptr, P5buf2.length, ((imm3 < 10) ? "%s #%d" : "#0x%X"), extend, imm3); + p5 = P5buf2.ptr; + + if (opS == 1 && Rd == 31) + { + p1 = "cmn"; // https://www.scs.stanford.edu/~zyedidia/arm64/cmn_adds_addsub_ext.html + shiftP(); + } + else if (opS == 3 && Rd == 0) + { + p1 = "cmp"; // https://www.scs.stanford.edu/~zyedidia/arm64/cmp_subs_addsub_ext.html + shiftP(); + } + } + else if (field(ins, 28, 21) == 0xD0 && field(ins, 15, 10) == 0) // https://www.scs.stanford.edu/~zyedidia/arm64/encodingindex.html#addsub_carry + { + if (log) printf("Add/subtract (with carry)\n"); + uint sf = field(ins, 31, 31); + uint op = field(ins, 30, 30); + uint S = field(ins, 29, 29); + uint Rm = field(ins, 20, 16); + uint Rn = field(ins, 9, 5); + uint Rd = field(ins, 4, 0); + + immutable char*[4] opstring = [ "adc", "adcs", "sbc", "sbcs" ]; + p1 = opstring[op * 2 + S]; + p2 = regString(sf, Rd); + p3 = regString(sf, Rn); + p4 = regString(sf, Rm); + } + else if (field(ins, 28, 21) == 0xD0 && field(ins, 15, 13) == 1) // http://www.scs.stanford.edu/~zyedidia/arm64/encodingindex.html#addsub_pt + { + if (log) printf("Add/subtract (checked pointer)\n"); + uint sf = field(ins, 31, 31); + uint op = field(ins, 30, 30); + uint S = field(ins, 29, 29); + uint Rm = field(ins, 20, 16); + uint imm3 = field(ins, 12, 10); + uint Rn = field(ins, 9, 5); + uint Rd = field(ins, 4, 0); + + uint sfopS = field(ins, 31, 29); + if (sfopS == 4 || sfopS == 6) + { + immutable char*[4] opstring = [ "adc", "adcs", "sbc", "sbcs" ]; + p1 = sfopS == 4 ? "addpt" : "subpt"; + p2 = regString(sf, Rd); + p3 = regString(sf, Rn); + p4 = regString(sf, Rm); + if (imm3) + { + __gshared char[7 + imm3.sizeof * 3 + 1] P5buf3 = void; + size_t n = snprintf(P5buf3.ptr, P5buf3.length, ((imm3 < 10) ? "LSL #%d" : "LSL #0x%X"), imm3); + assert(n <= P5buf3.length); + p5 = P5buf3.ptr; + } + } + } + else if (field(ins, 28, 21) == 0xD0 && field(ins, 14, 10) == 1) // http://www.scs.stanford.edu/~zyedidia/arm64/encodingindex.html#rmif + { + if (log) printf("Rotate right into flags\n"); + uint sf = field(ins, 31, 31); + uint op = field(ins, 30, 30); + uint S = field(ins, 29, 29); + uint imm3 = field(ins, 20, 15); + uint Rn = field(ins, 9, 5); + uint o2 = field(ins, 4, 4); + uint mask = field(ins, 3, 0); + + if (sf == 1 && op == 0 && S == 1 && o2 == 0) + { + p1 = "rmif"; + p2 = regString(sf, Rn); + p3 = wordtostring(imm3); + p4 = wordtostring2(mask); + } + } + else if (field(ins, 28, 21) == 0xD0 && field(ins, 13, 10) == 2) // http://www.scs.stanford.edu/~zyedidia/arm64/encodingindex.html#setf + { + if (log) printf("Evaluate into flags\n"); + uint sf = field(ins, 31, 31); + uint op = field(ins, 30, 30); + uint S = field(ins, 29, 29); + uint opcode2= field(ins, 20, 15); + uint sz = field(ins, 2, 2); + uint Rn = field(ins, 9, 5); + uint o3 = field(ins, 4, 4); + uint mask = field(ins, 3, 0); + + if (sf == 0 && op == 0 && S == 1 && opcode2 == 0 && o3 == 1 && mask == 0xD) + { + p1 = sz ? "setf16" : "setf8"; + p2 = regString(sf, Rn); + } + } + else if (field(ins, 28, 21) == 0xD2 && field(ins, 11, 11) == 0) + { + if (log) printf("Conditional compare (register)\n"); // http://www.scs.stanford.edu/~zyedidia/arm64/encodingindex.html#condcmp_reg + if (log) printf("Conditional compare (immediate)\n"); // http://www.scs.stanford.edu/~zyedidia/arm64/encodingindex.html#condcmp_imm + uint sf = field(ins, 31, 31); + uint op = field(ins, 30, 30); + uint S = field(ins, 29, 29); + uint Rm = field(ins, 20, 16); + uint imm5 = Rm; + uint cond = field(ins, 15, 12); + uint o2 = field(ins, 10, 10); + uint Rn = field(ins, 9, 5); + uint o3 = field(ins, 4, 4); + uint nzcv = field(ins, 3, 0); + + if (S == 1 && o2 == 0 && o3 == 0) + { + p1 = sf * 2 + op ? "ccmn" : "ccmp"; + p2 = regString(sf, Rn); + p3 = field(ins, 11, 11) ? wordtostring(imm5) : regString(sf, Rm); + p4 = wordtostring(nzcv); + p5 = condstring[cond]; + } + } + else if (field(ins, 28, 21) == 0xD4) // http://www.scs.stanford.edu/~zyedidia/arm64/encodingindex.html#condsel + { + if (log) printf("Conditional select\n"); + uint sf = field(ins, 31, 31); + uint op = field(ins, 30, 30); + uint S = field(ins, 29, 29); + uint Rm = field(ins, 20, 16); + uint cond = field(ins, 15, 12); + uint op2 = field(ins, 11, 10); + uint Rn = field(ins, 9, 5); + uint Rd = field(ins, 4, 0); + + immutable char*[4] opstring = [ "csel", "csinc", "csinv", "csneg" ]; + p1 = opstring[op * 2 + (op2 & 1)]; + p2 = regString(sf, Rd); + p3 = regString(sf, Rn); + p4 = regString(sf, Rm); + p5 = condstring[cond]; + } + else if (field(ins, 28, 24) == 0x1B) // http://www.scs.stanford.edu/~zyedidia/arm64/encodingindex.html#dp_3src + { + if (log) printf("Data-processing (3 source)\n"); + uint sf = field(ins, 31, 31); + uint op54 = field(ins, 30, 29); + uint op31 = field(ins, 23, 21); + uint Rm = field(ins, 20, 16); + uint oO = field(ins, 15, 15); + uint Ra = field(ins, 14, 10); + uint Rn = field(ins, 9, 5); + uint Rd = field(ins, 4, 0); + + if (op54 == 0) + { + if (op54 == 0 && op31 == 0) + { + p1 = oO ? "msub" : "madd"; + p2 = regString(sf, Rd); + p3 = regString(sf, Rn); + p4 = regString(sf, Rm); + p5 = regString(sf, Ra); + } + else if (sf) + { + uint Y(uint op31, uint oO) { return op31 * 2 + oO; } + switch (Y(op31, oO)) + { + case Y(1, 0): p1 = "smaddl"; goto Lxwwx; + case Y(1, 1): p1 = "smsubl"; goto Lxwwx; + case Y(2, 0): p1 = "smulh"; goto Lxxx; + case Y(3, 0): p1 = "maddpt"; goto Lxxxx; + case Y(3, 1): p1 = "msubpt"; goto Lxxxx; + case Y(5, 0): p1 = "umaddl"; goto Lxwwx; + case Y(5, 1): p1 = "umsubl"; goto Lxwwx; + case Y(6, 0): p1 = "umulh"; goto Lxxx; + + Lxwwx: + p2 = regString(sf, Rd); + p3 = regString( 0, Rn); + p4 = regString( 0, Rm); + p5 = regString(sf, Ra); + break; + + Lxxx: + p2 = regString(sf, Rd); + p3 = regString(sf, Rn); + p4 = regString(sf, Rm); + break; + + Lxxxx: + p2 = regString(sf, Rd); + p3 = regString(sf, Rn); + p4 = regString(sf, Rm); + p5 = regString(sf, Ra); + break; + + default: break; + } + } + } + } + + //printf("%x\n", field(ins, 31, 25)); + //printf("p1: %s\n", p1); + + put(' '); + puts(p1); + if (*p2) + { + for (int len1 = cast(int)strlen(p1); len1 < 9; ++len1) + put(' '); + put(' '); + puts(s2); + if (*p2 != ' ') + puts(p2); + if (*p3) + { + puts(sep); + puts(s3); + puts(p3); + if (*p4) + { + put(','); + puts(p4); + if (*p5) + { + put(','); + puts(p5); + if (*p6) + { + put(','); + puts(p6); + if (*p7) + { + put(','); + puts(p7); + } + } + } + } + } + } +} + +} + +/*********************** + * Default version. + * Creates string representation of memory location. + * Params: + * c = the address of the memory reference in `code[]` + * sz = the number of bytes in the referred to memory location + * offset = the value to be added to any symbolic reference + * Returns: + * string representation of the memory address + */ +@trusted +const(char)* memoryDefault(uint c, uint sz, addr offset) +{ + __gshared char[12 + 1] EA; + snprintf(EA.ptr,EA.length,"[0%Xh]",offset); + return EA.ptr; +} + +/*********************** + * Default version. + * Creates string representation of immediate value. + * Params: + * code = the binary instructions + * c = the address of the memory reference in `code[]` + * sz = the number of bytes in the instruction that form the referenece (2/4/8) + * Returns: + * string representation of the memory address + */ +@trusted +const(char)* immed16Default(ubyte[] code, uint c, int sz) +{ + ulong offset; + switch (sz) + { + case 8: + offset = dword(code, c) + (cast(ulong)dword(code, c + 4) << 32); + break; + + case 4: + offset = dword(code, c); + break; + + case 2: + offset = word(code, c); + break; + + default: + assert(0); + } + __gshared char[1 + offset.sizeof * 3 + 1 + 1] buf; + + snprintf(buf.ptr, buf.length,((cast(long)offset < 10) ? "%lld" : "0%llXh"), offset); + return buf.ptr; +} + +/*********************** + * Default version. + * Creates string representation of code label. + * Params: + * c = the address of the code reference to the label in `code[]` + * offset = address of the label in `code[]` + * farflag = if `far` reference + * is16bit = if 16 bit reference + * Returns: + * string representation of the memory address + */ +@trusted +const(char)* labelcodeDefault(uint c, uint offset, bool farflag, bool is16bit) +{ + //printf("offset = %x\n", offset); + __gshared char[1 + uint.sizeof * 3 + 1] buf; + snprintf(buf.ptr, buf.length, "L%x", offset); + return buf.ptr; +} + +/*********************** + * Default version. + * Params: + * pc = program counter + * offset = add to pc to get address of target + * Returns: + * string representation of the memory address + */ +@trusted +const(char)* shortlabelDefault(uint pc, int offset) +{ + __gshared char[1 + ulong.sizeof * 3 + 1] buf; + snprintf(buf.ptr, buf.length, "L%x", pc + offset); + return buf.ptr; +} + +/***************************** + * Load word at code[c]. + */ + +uint word(ubyte[] code, uint c) @safe +{ + return code[c] + (code[c + 1] << 8); +} + +/***************************** + * Load dword at code[c]. + */ + +addr dword(ubyte[] code, uint c) +{ + return word(code, c) + (cast(addr) word(code, c + 2) << 16); +} + +/************************************* + */ +@trusted +const(char)* wordtostring(uint w) +{ + __gshared char[1 + 3 + w.sizeof * 3 + 1 + 1] EA; + + snprintf(EA.ptr, EA.length, ((w < 10) ? "#%ld" : "#0x%lX"), w); + return EA.ptr; +} + +@trusted +const(char)* wordtostring(ulong w) +{ + __gshared char[1 + 3 + w.sizeof * 3 + 1 + 1] EA; + + snprintf(EA.ptr, EA.length, ((w < 10) ? "#%lld" : "#0x%llX"), w); + return EA.ptr; +} + +@trusted +const(char)* wordtostring2(uint w) +{ + __gshared char[1 + 3 + w.sizeof * 3 + 1 + 1] EA; + + snprintf(EA.ptr, EA.length, ((w < 10) ? "#%ld" : "#0x%lX"), w); + return EA.ptr; +} + +@trusted +const(char)* labeltostring(ulong w) +{ + __gshared char[2 + w.sizeof * 3 + 1] EA; + + auto n = snprintf(EA.ptr, EA.length, ((w < 10) ? "%lld" : "0x%llX"), w); + assert(n <= EA.length); + return EA.ptr; +} + + +/*************************************** + */ +pragma(inline, false) +const(char)* regString(uint sf, uint reg) { return sf ? xregs[reg] : wregs[reg]; } + +pragma(inline, false) +const(char)* cregString(uint reg) { return cregs[reg]; } + +immutable +{ + char*[32] xregs = [ "x0","x1","x2","x3","x4","x5","x6","x7", + "x8","x9","x10","x11","x12","x13","x14","x15", + "x16","x17","x18","x19","x20","x21","x22","x23", + "x24","x25","x26","x27","x28","x29","x30","sp" ]; + char*[32] wregs = [ "w0","w1","w2","w3","w4","w5","w6","w7", + "w8","w9","w10","w11","w12","w13","w14","w15", + "w16","w17","w18","w19","w20","w21","w22","w23", + "w24","w25","w26","w27","w28","w29","w30","wsp" ]; + char*[32] cregs = [ "c0","c1","c2","c3","c4","c5","c6","c7", + "c8","c9","c10","c11","c12","c13","c14","c15", + "c16","c17","c18","c19","c20","c21","c22","c23", + "c24","c25","c26","c27","c28","c29","c30","csp" ]; +} + +/****************************** + * Extract fields from instruction in manner lifted from spec. + * Params: + * opcode = opcode to extract field + * leftBit = leftmost bit number 31..0 + * rightBit = rightmost bit number 31..0 + * Returns: + * extracted field + */ +uint field(uint opcode, uint end, uint start) +{ + assert(end < 32 && start < 32 && start <= end); + //printf("%08x\n", (cast(uint)((cast(ulong)1 << (end + 1)) - 1) & opcode) >> start); + return (cast(uint)((cast(ulong)1 << (end + 1)) - 1) & opcode) >> start; +} + +unittest +{ + assert(field(0xFFFF_FFFF, 31, 31) == 1); + assert(field(0xFFFF_FFFF, 31, 0) == 0xFFFF_FFFF); + assert(field(0x0000_FFCF, 7, 4) == 0x0000_000C); +} + +/************************************* Tests ***********************************/ + +unittest +{ + int line64 = __LINE__; + string[27] cases64 = // 64 bit code gen + [ + "EB 03 08 9F cmp x4,x3,lsl #2", + "F1 00 08 7F cmp x3,#2", + "91 00 0C 00 add x0,x0,#3", + "9A C1 28 02 asr x2,x0,x1", + "93 43 FC 01 asr x1,x0,#3", + "93 C1 0C 03 extr x3,x0,x1,#3", + "13 81 0C 03 extr w3,w0,w1,#3", + "D3 7D F0 01 lsl x1,x0,#3", + "9A C1 20 02 lsl x2,x0,x1", + "9A C1 24 02 lsr x2,x0,x1", + "D3 43 FC 01 lsr x1,x0,#3", + "D2 80 01 C0 mov x0,#0xE", + "92 80 01 A1 mov x1,#0xFFFFFFFFFFFFFFF2", + "D2 80 02 02 mov x2,#0x10", + "D2 80 01 C3 mov x3,#0xE", + "D2 80 07 04 mov x4,#0x38", + "9A C1 2C 02 ror x2,x0,x1", + "93 C0 0C 01 ror x1,x0,#3", + "93 C0 0C 03 ror x3,x0,#3", + "D6 5F 03 C0 ret", + "D6 5F 0B FF retaa", + "D6 5F 0F FF retab", + "D6 5F 0B F3 retaasppc x19", + "D6 5F 0F F4 retabsppc x20", + "13 14 3C 24 sbfiz w4,w1,#0xC,#0x10", + "13 00 1C 24 sxtb w4,w1", + "13 00 3C 24 sxth w4,w1", + ]; + + char[BUFMAX] buf; + ubyte[BUFMAX] buf2; + bool errors; + + void testcase(int line, string s, uint size) + { + //printf("testcase(line %d s: '%.*s'\n", cast(int)line, cast(int)s.length, s.ptr); + auto codput = Output!ubyte(buf2[]); + size_t j; + ubyte[] code = hexToUbytes(codput, j, s); + + if (code.length != 4) + { + printf("Fail%d: %d hex code must be 4 bytes was %d\n", + size, cast(int)(line + 2), cast(int)code.length); + errors = true; + return; + } + // Reverse code[] + ubyte c = code[0]; code[0] = code[3]; code[3] = c; + c = code[1]; code[1] = code[2]; code[2] = c; + + string expected = s[j .. $]; + + addr m; + auto length = calccodsize(code, 0, m, size); + assert(length == 4); + + auto output = Output!char(buf[]); + getopstring(&output.put, code, 0, length, + size, 0, 0, null, null, null, null); + auto result = output.peek(); + + static bool compareEqual(const(char)[] result, const(char)[] expected) + { + size_t r, e; + while (1) + { + while (r < result.length && (result[r] == ' ' || result[r] == '\t')) + ++r; + while (e < expected.length && (expected[e] == ' ' || expected[e] == '\t')) + ++e; + + if ((r == result.length) != (e == expected.length)) + return false; + + if (r == result.length) + return true; + + if (result[r] != expected[e]) + return false; + + ++r; + ++e; + } + } + + if (!compareEqual(result, expected)) + { + printf("Fail%d: %d expected '%.*s' got '%.*s'\n", + size, cast(int)(line + 2), + cast(int)expected.length, expected.ptr, cast(int)result.length, result.ptr); + errors = true; + } + } + + foreach (i; 0 .. cases64.length) + testcase(line64, cases64[i], 64); + + assert(!errors); +} + +version (unittest) +{ + +/********************** + * Converts hex string prefix in `s` in test cases to ubyte[] + * Params: + * output = where to write the ubyte's + * m = index of start of expected result + * s = ascii source + * Returns: + * converted ubyte[] + */ +ubyte[] hexToUbytes(ref Output!ubyte output, out size_t m, string s) +{ + uint n = 0; + ubyte v = 0; + + Loop: + foreach (i, cc; s) + { + m = i; + char c = cc; + switch (c) + { + case ' ': + case '\t': + case '\v': + case '\f': + case '\r': + case '\n': + continue; // skip white space + + case 0: + case 0x1A: + printf("unterminated string constant at %d\n", cast(int)i); + assert(0); + + case '0': .. case '9': + c -= '0'; + break; + + case 'A': .. case 'F': + c -= 'A' - 10; + break; + + default: + break Loop; + } + if (n & 1) + { + v = cast(ubyte)((v << 4) | c); + output.put(v); + v = 0; + } + else + v = c; + ++n; + } + if (n & 1) + { + printf("unterminated string constant\n"); + assert(0); + } + return output.peek; +} + +struct Output(T) +{ + nothrow @nogc: + + T[] buf; + size_t i; + + void put(T c) + { + buf[i] = c; + ++i; + } + + void initialize(T[] buf) + { + this.buf = buf; + i = 0; + } + + T[] peek() + { + return buf[0 .. i]; + } +} + +} diff --git a/compiler/src/dmd/backend/backconfig.d b/compiler/src/dmd/backend/backconfig.d index 47cb1b77cc1e..b00a128888ea 100644 --- a/compiler/src/dmd/backend/backconfig.d +++ b/compiler/src/dmd/backend/backconfig.d @@ -29,6 +29,7 @@ nothrow: /************************************** * Initialize configuration for backend. * Params: + arm = true for generating Aarch64 code model = 32 for 32 bit code, 64 for 64 bit code, set bit 0 to generate MS-COFF instead of OMF on Windows @@ -60,6 +61,7 @@ nothrow: public @trusted extern (C) void out_config_init( + bool arm, // true for generating Aarch64 code int model, bool exe, bool trace, @@ -87,6 +89,8 @@ extern (C) void out_config_init( auto cfg = &config; cfg._version = _version; + if (arm) + cfg.target_cpu = TARGET_Aarch64; if (!cfg.target_cpu) { cfg.target_cpu = TARGET_PentiumPro; cfg.target_scheduler = cfg.target_cpu; diff --git a/compiler/src/dmd/backend/cdef.d b/compiler/src/dmd/backend/cdef.d index ba7a9c50fcbf..8b5b3786a094 100644 --- a/compiler/src/dmd/backend/cdef.d +++ b/compiler/src/dmd/backend/cdef.d @@ -228,6 +228,7 @@ enum TARGET_PentiumMMX = 6, TARGET_PentiumPro = 7, TARGET_PentiumII = 8, + TARGET_Aarch64 = 9, } // Symbolic debug info diff --git a/compiler/src/dmd/backend/code.d b/compiler/src/dmd/backend/code.d index e90d50a367bc..6f4a2c6b8f60 100644 --- a/compiler/src/dmd/backend/code.d +++ b/compiler/src/dmd/backend/code.d @@ -226,7 +226,7 @@ struct CGstate con_t regcon; // register contents BackendPass pass; - int cmp_flag; // pass extra flag from cdcod() to cdcmp() + int cmp_flag; // pass extra flag from cdcod() to cdcmp() /********************************** * Set value in regimmed for reg. * NOTE: For 16 bit generator, this is always a (targ_short) sign-extended diff --git a/compiler/src/dmd/backend/dcgcv.d b/compiler/src/dmd/backend/dcgcv.d index 790c3c65a6a5..07df5e5299b9 100644 --- a/compiler/src/dmd/backend/dcgcv.d +++ b/compiler/src/dmd/backend/dcgcv.d @@ -509,6 +509,8 @@ void cv_init() case TARGET_PentiumPro: case TARGET_PentiumII: debsym[4] = 6; break; + case TARGET_Aarch64: + debsym[4] = 7; break; // made that up default: assert(0); } debsym[5] = (CPP != 0); // 0==C, 1==C++ diff --git a/compiler/src/dmd/backend/melf.d b/compiler/src/dmd/backend/melf.d index 929decaa996e..dda726d53017 100644 --- a/compiler/src/dmd/backend/melf.d +++ b/compiler/src/dmd/backend/melf.d @@ -8,6 +8,7 @@ * Translation to D of Linux's melf.h * * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/backend/melf.d, backend/melf.d) + * References: $(LINK2 https://github.com/ARM-software/abi-aa/blob/main/aaelf64/aaelf64.rst, aaelf64) */ module dmd.backend.melf; @@ -76,6 +77,7 @@ nothrow: enum EM_386 = 3; /* Intel 80386 */ enum EM_486 = 6; /* Intel 80486 */ enum EM_X86_64 = 62; // Advanced Micro Devices X86-64 processor + enum EM_AARCH64 = 183; // AMD Aarch64 // e_version enum EV_NONE = 0; // invalid version diff --git a/compiler/src/dmd/backend/x86/cgcod.d b/compiler/src/dmd/backend/x86/cgcod.d index de1b6619284f..9b73fbb21750 100644 --- a/compiler/src/dmd/backend/x86/cgcod.d +++ b/compiler/src/dmd/backend/x86/cgcod.d @@ -40,6 +40,8 @@ import dmd.backend.symtab; import dmd.backend.ty; import dmd.backend.type; +import dmd.backend.arm.disasmarm; + import dmd.backend.x86.code_x86; import dmd.backend.x86.xmm; @@ -582,6 +584,9 @@ private @trusted void prolog(ref CGstate cg, ref CodeBuilder cdb) { + if (config.target_cpu == TARGET_Aarch64) + return; // not implemented + bool enter; //printf("cgcod.prolog() %s, needframe = %d, Auto.alignment = %d\n", funcsym_p.Sident.ptr, cg.needframe, cg.Auto.alignment); @@ -3033,19 +3038,33 @@ private extern (D) void disassemble(ubyte[] code) { printf("%s:\n", funcsym_p.Sident.ptr); - const model = I16 ? 16 : I32 ? 32 : 64; // 16/32/64 - size_t i = 0; - while (i < code.length) - { - printf("%04x:", cast(int)i); - uint pc; - const sz = dmd.backend.disasm86.calccodsize(code, cast(uint)i, pc, model); - void put(char c) { printf("%c", c); } + void put(char c) { printf("%c", c); } + + if (config.target_cpu == TARGET_Aarch64) + { + for (size_t i = 0; i < code.length; i += 4) + { + printf("%04x:", cast(int)i); + dmd.backend.arm.disasmarm.getopstring(&put, code, cast(uint)i, 4, 64, false, true, + null, null, null, null); + printf("\n"); + } + } + else + { + const model = I16 ? 16 : I32 ? 32 : 64; // 16/32/64 + size_t i = 0; + while (i < code.length) + { + printf("%04x:", cast(int)i); + uint pc; + const sz = dmd.backend.disasm86.calccodsize(code, cast(uint)i, pc, model); - dmd.backend.disasm86.getopstring(&put, code, cast(uint)i, sz, model, model == 16, true, - null, null, null, null); - printf("\n"); - i += sz; + dmd.backend.disasm86.getopstring(&put, code, cast(uint)i, sz, model, model == 16, true, + null, null, null, null); + printf("\n"); + i += sz; + } } } diff --git a/compiler/src/dmd/backend/x86/cod3.d b/compiler/src/dmd/backend/x86/cod3.d index 6c1de2b90c78..1f26f9227f08 100644 --- a/compiler/src/dmd/backend/x86/cod3.d +++ b/compiler/src/dmd/backend/x86/cod3.d @@ -30,6 +30,7 @@ import dmd.backend.cc; import dmd.backend.cdef; import dmd.backend.cgcse; import dmd.backend.code; +import dmd.backend.arm.cod3; import dmd.backend.x86.code_x86; import dmd.backend.codebuilder; import dmd.backend.dlist; @@ -4313,6 +4314,9 @@ void prolog_loadparams(ref CodeBuilder cdb, tym_t tyf, bool pushalloc) @trusted void epilog(block *b) { + if (config.target_cpu == TARGET_Aarch64) + return dmd.backend.arm.cod3.epilog(b); + code *cpopds; reg_t reg; reg_t regx; // register that's not a return reg @@ -6382,6 +6386,9 @@ uint calcblksize(code *c) @trusted uint calccodsize(code *c) { + if (config.target_cpu == TARGET_Aarch64) + return 4; + uint size; ubyte rm,mod,ins; uint iflags; @@ -6690,7 +6697,7 @@ nomatch: * Little buffer allocated on the stack to accumulate instruction bytes to * later be sent along to objmod */ -private struct MiniCodeBuf +struct MiniCodeBuf { nothrow: uint index; @@ -6724,6 +6731,9 @@ nothrow: void gen(ubyte c) { bytes[index++] = c; } + @trusted + void gen32(uint c) { assert(index <= bytes.length - 4); *(cast(uint*)(&bytes[index])) = c; index += 4; } + @trusted void genp(uint n, void *p) { memcpy(&bytes[index], p, n); index += n; } @@ -6787,6 +6797,9 @@ nothrow: @trusted uint codout(int seg, code *c, Barray!ubyte* disasmBuf) { + if (config.target_cpu == TARGET_Aarch64) + return dmd.backend.arm.cod3.codout(seg, c, disasmBuf); + ubyte rm,mod; ubyte ins; code *cn; @@ -7671,8 +7684,11 @@ void WRcodlst(code *c) } @trusted -extern (C) void code_print(scope code* c) +void code_print(scope code* c) { + if (config.target_cpu == TARGET_Aarch64) + return dmd.backend.arm.cod3.code_print(c); + ubyte ins; ubyte rexb; diff --git a/compiler/src/dmd/dmsc.d b/compiler/src/dmd/dmsc.d index af037bb19a23..2052c20bbf15 100644 --- a/compiler/src/dmd/dmsc.d +++ b/compiler/src/dmd/dmsc.d @@ -40,15 +40,16 @@ void backend_init() //printf("out_config_init()\n"); Param *params = &global.params; exefmt_t exfmt; + bool is64 = target.isX86_64 || target.isAarch64; switch (target.os) { - case Target.OS.Windows: exfmt = target.isX86_64 ? EX_WIN64 : EX_WIN32; break; - case Target.OS.linux: exfmt = target.isX86_64 ? EX_LINUX64 : EX_LINUX; break; - case Target.OS.OSX: exfmt = target.isX86_64 ? EX_OSX64 : EX_OSX; break; - case Target.OS.FreeBSD: exfmt = target.isX86_64 ? EX_FREEBSD64 : EX_FREEBSD; break; - case Target.OS.OpenBSD: exfmt = target.isX86_64 ? EX_OPENBSD64 : EX_OPENBSD; break; - case Target.OS.Solaris: exfmt = target.isX86_64 ? EX_SOLARIS64 : EX_SOLARIS; break; - case Target.OS.DragonFlyBSD: exfmt = EX_DRAGONFLYBSD64; break; + case Target.OS.Windows: exfmt = is64 ? EX_WIN64 : EX_WIN32; break; + case Target.OS.linux: exfmt = is64 ? EX_LINUX64 : EX_LINUX; break; + case Target.OS.OSX: exfmt = is64 ? EX_OSX64 : EX_OSX; break; + case Target.OS.FreeBSD: exfmt = is64 ? EX_FREEBSD64 : EX_FREEBSD; break; + case Target.OS.OpenBSD: exfmt = is64 ? EX_OPENBSD64 : EX_OPENBSD; break; + case Target.OS.Solaris: exfmt = is64 ? EX_SOLARIS64 : EX_SOLARIS; break; + case Target.OS.DragonFlyBSD: assert(is64); exfmt = EX_DRAGONFLYBSD64; break; default: assert(0); } @@ -66,7 +67,9 @@ void backend_init() exe = true; // if writing out EXE file out_config_init( - (target.isX86_64 ? 64 : 32) | (target.objectFormat() == Target.ObjectFormat.coff ? 1 : 0), + target.isAarch64, + (is64 ? 64 : 32) | + (target.objectFormat() == Target.ObjectFormat.coff), exe, false, //params.trace, driverParams.nofloat, diff --git a/compiler/src/dmd/frontend.h b/compiler/src/dmd/frontend.h index 69104aeea883..01a35dda0e32 100644 --- a/compiler/src/dmd/frontend.h +++ b/compiler/src/dmd/frontend.h @@ -7545,6 +7545,7 @@ struct Target final TargetObjC objc; _d_dynamicArray< const char > architectureName; CPU cpu; + bool isAarch64; bool isX86_64; bool isX86; bool isLP64; @@ -7617,6 +7618,7 @@ struct Target final cpp(), objc(), architectureName(), + isAarch64(), isX86_64(), isX86(), isLP64(), @@ -7631,7 +7633,7 @@ struct Target final params() { } - Target(OS os, uint8_t osMajor = 0u, uint8_t ptrsize = 0u, uint8_t realsize = 0u, uint8_t realpad = 0u, uint8_t realalignsize = 0u, uint8_t classinfosize = 0u, uint64_t maxStaticDataSize = 0LLU, TargetC c = TargetC(), TargetCPP cpp = TargetCPP(), TargetObjC objc = TargetObjC(), _d_dynamicArray< const char > architectureName = {}, CPU cpu = (CPU)0u, bool isX86_64 = false, bool isX86 = false, bool isLP64 = false, _d_dynamicArray< const char > obj_ext = {}, _d_dynamicArray< const char > lib_ext = {}, _d_dynamicArray< const char > dll_ext = {}, bool run_noext = false, FPTypeProperties FloatProperties = FPTypeProperties(), FPTypeProperties DoubleProperties = FPTypeProperties(), FPTypeProperties<_d_real > RealProperties = FPTypeProperties<_d_real >(), Type* tvalist = nullptr, const Param* params = nullptr) : + Target(OS os, uint8_t osMajor = 0u, uint8_t ptrsize = 0u, uint8_t realsize = 0u, uint8_t realpad = 0u, uint8_t realalignsize = 0u, uint8_t classinfosize = 0u, uint64_t maxStaticDataSize = 0LLU, TargetC c = TargetC(), TargetCPP cpp = TargetCPP(), TargetObjC objc = TargetObjC(), _d_dynamicArray< const char > architectureName = {}, CPU cpu = (CPU)0u, bool isAarch64 = false, bool isX86_64 = false, bool isX86 = false, bool isLP64 = false, _d_dynamicArray< const char > obj_ext = {}, _d_dynamicArray< const char > lib_ext = {}, _d_dynamicArray< const char > dll_ext = {}, bool run_noext = false, FPTypeProperties FloatProperties = FPTypeProperties(), FPTypeProperties DoubleProperties = FPTypeProperties(), FPTypeProperties<_d_real > RealProperties = FPTypeProperties<_d_real >(), Type* tvalist = nullptr, const Param* params = nullptr) : os(os), osMajor(osMajor), ptrsize(ptrsize), @@ -7645,6 +7647,7 @@ struct Target final objc(objc), architectureName(architectureName), cpu(cpu), + isAarch64(isAarch64), isX86_64(isX86_64), isX86(isX86), isLP64(isLP64), diff --git a/compiler/src/dmd/main.d b/compiler/src/dmd/main.d index 8ed5a59a7ff8..7e223cee25fb 100644 --- a/compiler/src/dmd/main.d +++ b/compiler/src/dmd/main.d @@ -836,7 +836,7 @@ bool parseCommandlineAndConfig(size_t argc, const(char)** argv, ref Param params if (char* p = getenv("DDOCFILE")) global.params.ddoc.files.shift(p); - if (target.isX86_64 != isX86_64) + if (target.isX86_64 != isX86_64 && !target.isAarch64) error(Loc.initial, "the architecture must not be changed in the %s section of %.*s", envsection.ptr, cast(int)global.inifilename.length, global.inifilename.ptr); diff --git a/compiler/src/dmd/mars.d b/compiler/src/dmd/mars.d index de106d56150d..80af5ae76c09 100644 --- a/compiler/src/dmd/mars.d +++ b/compiler/src/dmd/mars.d @@ -881,18 +881,27 @@ bool parseCommandLine(const ref Strings arguments, const size_t argc, ref Param { continue; // skip druntime options, e.g. used to configure the GC } + else if (arg == "-arm") // https://dlang.org/dmd.html#switch-arm + { + target.isAarch64 = true; + target.isX86 = false; + target.isX86_64 = false; + } else if (arg == "-m32") // https://dlang.org/dmd.html#switch-m32 { + target.isAarch64 = false; target.isX86 = true; target.isX86_64 = false; } else if (arg == "-m64") // https://dlang.org/dmd.html#switch-m64 { + target.isAarch64 = false; target.isX86 = false; target.isX86_64 = true; } else if (arg == "-m32mscoff") // https://dlang.org/dmd.html#switch-m32mscoff { + target.isAarch64 = false; target.isX86 = true; target.isX86_64 = false; } diff --git a/compiler/src/dmd/target.d b/compiler/src/dmd/target.d index e53291f8ec9f..ad3668d09682 100644 --- a/compiler/src/dmd/target.d +++ b/compiler/src/dmd/target.d @@ -222,7 +222,11 @@ void addPredefinedGlobalIdentifiers(const ref Target tgt) addCRuntimePredefinedGlobalIdent(tgt.c); addCppRuntimePredefinedGlobalIdent(tgt.cpp); - if (tgt.isX86_64) + if (tgt.isAarch64) + { + VersionCondition.addPredefinedGlobalIdent("Aarch64"); + } + else if (tgt.isX86_64) { VersionCondition.addPredefinedGlobalIdent("D_InlineAsm_X86_64"); VersionCondition.addPredefinedGlobalIdent("X86_64"); @@ -233,6 +237,7 @@ void addPredefinedGlobalIdentifiers(const ref Target tgt) VersionCondition.addPredefinedGlobalIdent("D_InlineAsm_X86"); VersionCondition.addPredefinedGlobalIdent("X86"); } + if (tgt.isLP64) VersionCondition.addPredefinedGlobalIdent("D_LP64"); else if (tgt.isX86_64) @@ -349,6 +354,7 @@ extern (C++) struct Target /// Architecture name const(char)[] architectureName; CPU cpu; // CPU instruction set to target + bool isAarch64; // generate 64 bit Arm code bool isX86_64; // generate 64 bit code for x86_64; true by default for 64 bit dmd bool isX86; // generate 32 bit Intel x86 code bool isLP64; // pointers are 64 bits @@ -406,7 +412,7 @@ extern (C++) struct Target extern (C++) void _init(ref const Param params) { // isX86_64 and cpu are initialized in parseCommandLine - isX86 = !isX86_64; + isX86 = !isX86_64; this.params = ¶ms; diff --git a/compiler/src/dmd/target.h b/compiler/src/dmd/target.h index a5caa74c80e8..907959ceafd5 100644 --- a/compiler/src/dmd/target.h +++ b/compiler/src/dmd/target.h @@ -153,6 +153,7 @@ struct Target DString architectureName; // name of the platform architecture (e.g. X86_64) CPU cpu; // CPU instruction set to target + d_bool isAarch64; // generate 64 bit Arm code d_bool isX86_64; // generate 64 bit code for x86_64; true by default for 64 bit dmd d_bool isX86; // generate 32 bit Intel x86 code d_bool isLP64; // pointers are 64 bits