get switch statements to work (#21159)

This commit is contained in:
Walter Bright 2025-04-06 09:17:35 -07:00 committed by GitHub
parent 8bce4f5a1a
commit 63ed7154b7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 60 additions and 24 deletions

View file

@ -1600,6 +1600,7 @@ void assignaddrc(code* c)
case FL.code:
case FL.unde:
case FL.block:
case FL.switch_:
break;
default:

View file

@ -1406,7 +1406,7 @@ void cdcnvt(ref CGstate cg, ref CodeBuilder cdb,elem* e, ref regm_t pretregs)
case OPu64_d: // ucvtf d31,x0
regm_t retregs1 = ALLREGS;
codelem(cgstate,cdb,e.E1,retregs1,false);
reg_t Rn = findreg(retregs1); // source integer register
reg_t Rn = findreg(retregs1); // source integer register
regm_t retregs = INSTR.FLOATREGS;
const tym = tybasic(e.Ety);

View file

@ -2252,16 +2252,20 @@ void disassemble(uint c) @trusted
case ldr(1,0,3): p1 = "ldrsh"; goto Lldr;
case ldr(2,0,0): p1 = "str"; goto Lldr;
case ldr(2,0,1): p1 = "ldr"; goto Lldr;
case ldr(2,0,2): p1 = "ldrsw"; goto Lldr64;
case ldr(2,0,2): p1 = "ldrsw"; goto Lldrsw;
case ldr(3,0,0): p1 = "str"; goto Lldr64;
case ldr(3,0,1): p1 = "ldr"; goto Lldr64;
//case ldr(3,0,2): p1 = "prfm";
Lldrsw:
p2 = regString(true, Rt);
p3 = eaString(0, cast(ubyte)Rn, imm12 * 4);
break;
Lldr64:
is64 = true;
Lldr:
p2 = regString(is64, Rt);
uint offset = imm12 * (is64 ? 8 : 4);
p3 = eaString(0, cast(ubyte)Rn, offset);
p3 = eaString(0, cast(ubyte)Rn, imm12 * (is64 ? 8 : 4));
break;
case ldr(0,1,0): p1 = "str"; goto LsimdFp;

View file

@ -937,7 +937,7 @@ void outblkexitcode(ref CodeBuilder cdb, block* bl, ref int anyspill, const(FL)*
case BC.switch_:
{
assert(!(bl.Bflags & BFL.epilog));
doswitch(cdb,bl); // hide messy details
doswitch(cgstate,cdb,bl); // hide messy details
break;
}
case BC.jcatch: // D catch clause of try-catch
@ -1598,9 +1598,20 @@ struct CaseVal
* Generate comparison of [reg2,reg] with val
*/
@trusted
private void cmpval(ref CodeBuilder cdb, targ_llong val, uint sz, reg_t reg, reg_t reg2, reg_t sreg)
private void cmpval(CGstate cg, ref CodeBuilder cdb, targ_llong val, uint sz, reg_t reg, reg_t reg2, reg_t sreg)
{
if (I64 && sz == 8)
if (cg.AArch64)
{
// TODO AArch64 use CMP https://www.scs.stanford.edu/~zyedidia/arm64/cmp_subs_addsub_imm.html
assert(sreg == NOREG);
regm_t retregs = cg.allregs & ~mask(reg);
sreg = allocreg(cdb,retregs,TYint);
movregconst(cdb,sreg,val,sz == 8 ? 64 : 0);
getregsNoSave(retregs);
assert(reg2 == NOREG);
cdb.gen1(INSTR.cmp_shift(sz == 8, reg, 0, 0, sreg)); // CMP sreg,reg
}
else if (I64 && sz == 8)
{
assert(reg2 == NOREG);
if (val == cast(int)val) // if val is a 64 bit value sign-extended from 32 bits
@ -1630,7 +1641,7 @@ private void cmpval(ref CodeBuilder cdb, targ_llong val, uint sz, reg_t reg, reg
}
@trusted extern (D)
private void ifthen(ref CodeBuilder cdb, scope CaseVal[] casevals,
private void ifthen(CGstate cg, ref CodeBuilder cdb, scope CaseVal[] casevals,
uint sz, reg_t reg, reg_t reg2, reg_t sreg, block* bdefault, bool last)
{
const ncases = casevals.length;
@ -1640,18 +1651,27 @@ private void ifthen(ref CodeBuilder cdb, scope CaseVal[] casevals,
// Compares for casevals[0..pivot]
CodeBuilder cdb1; cdb1.ctor();
ifthen(cdb1, casevals[0 .. pivot], sz, reg, reg2, sreg, bdefault, true);
ifthen(cg, cdb1, casevals[0 .. pivot], sz, reg, reg2, sreg, bdefault, true);
// Compares for casevals[pivot+1..ncases]
CodeBuilder cdb2; cdb2.ctor();
ifthen(cdb2, casevals[pivot + 1 .. $], sz, reg, reg2, sreg, bdefault, last);
ifthen(cg, cdb2, casevals[pivot + 1 .. $], sz, reg, reg2, sreg, bdefault, last);
code* c2 = gennop(null);
// Compare for caseval[pivot]
cmpval(cdb, casevals[pivot].val, sz, reg, reg2, sreg);
genjmp(cdb,JE,FL.block,casevals[pivot].target); // JE target
// Note uint jump here, as cases were sorted using uint comparisons
genjmp(cdb,JA,FL.code,cast(block*) c2); // JG c2
cmpval(cg, cdb, casevals[pivot].val, sz, reg, reg2, sreg);
if (cg.AArch64)
{
genBranch(cdb,COND.eq,FL.block,casevals[pivot].target); // equal
// Note uint jump here, as cases were sorted using uint comparisons
genBranch(cdb,COND.cs,FL.code,cast(block*) c2); // greater than
}
else
{
genjmp(cdb,JE,FL.block,casevals[pivot].target); // JE target
// Note uint jump here, as cases were sorted using uint comparisons
genjmp(cdb,JA,FL.code,cast(block*) c2); // JG c2
}
cdb.append(cdb1);
cdb.append(c2);
@ -1662,20 +1682,29 @@ private void ifthen(ref CodeBuilder cdb, scope CaseVal[] casevals,
foreach (size_t n; 0 .. ncases)
{
targ_llong val = casevals[n].val;
cmpval(cdb, val, sz, reg, reg2, sreg);
cmpval(cg, cdb, val, sz, reg, reg2, sreg);
code* cnext = null;
if (reg2 != NOREG)
{
assert(!cg.AArch64);
cnext = gennop(null);
genjmp(cdb,JNE,FL.code,cast(block*) cnext); // JNE cnext
cdb.genc2(0x81,modregrm(3,7,reg2),cast(targ_size_t)MSREG(val)); // CMP reg2,MSREG(casevalue)
}
genjmp(cdb,JE,FL.block,casevals[n].target); // JE caseaddr
if (cg.AArch64)
genBranch(cdb,COND.eq,FL.block,casevals[n].target); // JE caseaddr
else
genjmp(cdb,JE,FL.block,casevals[n].target); // JE caseaddr
cdb.append(cnext);
}
if (last) // if default is not next block
genjmp(cdb,JMP,FL.block,bdefault);
{
if (cg.AArch64)
genBranch(cdb,COND.al,FL.block,bdefault);
else
genjmp(cdb,JMP,FL.block,bdefault);
}
}
}
@ -1688,7 +1717,7 @@ private void ifthen(ref CodeBuilder cdb, scope CaseVal[] casevals,
*/
@trusted
void doswitch(ref CodeBuilder cdb, block* b)
void doswitch(ref CGstate cg, ref CodeBuilder cdb, block* b)
{
// If switch tables are in code segment and we need a CS: override to get at them
bool csseg = cast(bool)(config.flags & CFGromable);
@ -1732,7 +1761,7 @@ void doswitch(ref CodeBuilder cdb, block* b)
/* Three kinds of switch strategies - pick one
*/
const ncases = b.Bswitch.length;
if (ncases <= 3)
if (ncases <= 3 || cg.AArch64)
goto Lifthen;
else if (I16 && cast(targ_ullong)(vmax - vmin) <= ncases * 2)
goto Ljmptab; // >=50% of the table is case values, rest is default
@ -1748,7 +1777,8 @@ void doswitch(ref CodeBuilder cdb, block* b)
/*************************************************************************/
{ // generate if-then sequence
Lifthen:
regm_t retregs = ALLREGS;
//printf("ifthen:\n");
regm_t retregs = cg.allregs;
b.bc = BC.ifthen;
scodelem(cgstate,cdb,e,retregs,0,true);
reg_t reg, reg2;
@ -1786,8 +1816,8 @@ void doswitch(ref CodeBuilder cdb, block* b)
casevals[n].target = list_block(bl);
// See if we need a scratch register
if (sreg == NOREG && I64 && sz == 8 && val != cast(int)val)
{ regm_t regm = ALLREGS & ~mask(reg);
if (!cg.AArch64 && sreg == NOREG && I64 && sz == 8 && val != cast(int)val)
{ regm_t regm = cg.allregs & ~mask(reg);
sreg = allocreg(cdb,regm, TYint);
}
}
@ -1799,7 +1829,7 @@ void doswitch(ref CodeBuilder cdb, block* b)
//printf("casevals[%lld] = x%x\n", n, casevals[n].val);
// Generate binary tree of comparisons
ifthen(cdb, casevals, sz, reg, reg2, sreg, bdefault, bdefault != b.Bnext);
ifthen(cg, cdb, casevals, sz, reg, reg2, sreg, bdefault, bdefault != b.Bnext);
cgstate.stackclean--;
return;
@ -1809,7 +1839,7 @@ void doswitch(ref CodeBuilder cdb, block* b)
{
// Use switch value to index into jump table
Ljmptab:
//printf("Ljmptab:\n");
//printf("jmptab:\n");
b.bc = BC.jmptab;
@ -2013,6 +2043,7 @@ else
* Note that it has not been tested with MACHOBJ (OSX).
*/
Lswitch:
//printf("repne scasw:\n");
regm_t retregs = mAX; // SCASW requires AX
if (dword)
retregs |= mDX;